From d70fb9099fb9f33c62fddde0a50dbaba5977aec6 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Fri, 23 Nov 2018 10:31:39 +0100 Subject: [PATCH 01/16] add sonoff-tasmota plugin --- nymea-plugins.pro | 75 +++--- tasmota/deviceplugintasmota.cpp | 231 ++++++++++++++++++ tasmota/deviceplugintasmota.h | 66 +++++ tasmota/deviceplugintasmota.json | 220 +++++++++++++++++ tasmota/tasmota.pro | 11 + ...6e0c0-0cbf-4731-aabb-b2201088d6cb-en_US.ts | 4 + 6 files changed, 570 insertions(+), 37 deletions(-) create mode 100644 tasmota/deviceplugintasmota.cpp create mode 100644 tasmota/deviceplugintasmota.h create mode 100644 tasmota/deviceplugintasmota.json create mode 100644 tasmota/tasmota.pro create mode 100644 tasmota/translations/d136e0c0-0cbf-4731-aabb-b2201088d6cb-en_US.ts diff --git a/nymea-plugins.pro b/nymea-plugins.pro index c5288ed2..2afdb106 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -1,46 +1,47 @@ TEMPLATE = subdirs PLUGIN_DIRS = \ - elro \ - intertechno \ - networkdetector \ - conrad \ - openweathermap \ - wakeonlan \ - mailnotification \ - philipshue \ - eq-3 \ - wemo \ - lgsmarttv \ - datetime \ - genericelements \ - commandlauncher \ - unitec \ - leynew \ - udpcommander \ - tcpcommander \ - httpcommander \ - kodi \ - elgato \ - senic \ - awattar \ - netatmo \ - plantcare \ - osdomotics \ - ws2812 \ - orderbutton \ - denon \ avahimonitor \ - pushbullet \ - usbwde \ - multisensor \ - gpio \ - snapd \ - simulation \ - keba \ - remotessh \ + awattar \ + commandlauncher \ + conrad \ + datetime \ + denon \ dweetio \ + elgato \ + elro \ + eq-3 \ flowercare \ + genericelements \ + gpio \ + httpcommander \ + intertechno \ + keba \ + kodi \ + leynew \ + lgsmarttv \ + mailnotification \ + multisensor \ + netatmo \ + networkdetector \ + openweathermap \ + orderbutton \ + osdomotics \ + philipshue \ + plantcare \ + pushbullet \ + remotessh \ + senic \ + simulation \ + snapd \ + tasmota \ + tcpcommander \ + udpcommander \ + unitec \ + usbwde \ + wakeonlan \ + wemo \ + ws2812 \ CONFIG+=all diff --git a/tasmota/deviceplugintasmota.cpp b/tasmota/deviceplugintasmota.cpp new file mode 100644 index 00000000..c684e8aa --- /dev/null +++ b/tasmota/deviceplugintasmota.cpp @@ -0,0 +1,231 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Michael Zanetti * + * * + * 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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "deviceplugintasmota.h" +#include "plugininfo.h" + +#include +#include +#include +#include + +#include "hardwaremanager.h" +#include "network/networkaccessmanager.h" +#include "network/mqtt/mqttprovider.h" +#include "network/mqtt/mqttchannel.h" + +DevicePluginTasmota::DevicePluginTasmota() +{ + // Helper maps for parent devices (aka sonoff_*) + m_ipAddressParamTypeMap[sonoff_basicDeviceClassId] = sonoff_basicDeviceIpAddressParamTypeId; + m_ipAddressParamTypeMap[sonoff_dualDeviceClassId] = sonoff_dualDeviceIpAddressParamTypeId; + m_ipAddressParamTypeMap[sonoff_quadDeviceClassId] = sonoff_quadDeviceIpAddressParamTypeId; + + m_attachedDeviceParamTypeIdMap[sonoff_basicDeviceClassId] << sonoff_basicDeviceAttachedDeviceCH1ParamTypeId; + m_attachedDeviceParamTypeIdMap[sonoff_dualDeviceClassId] << sonoff_dualDeviceAttachedDeviceCH1ParamTypeId << sonoff_dualDeviceAttachedDeviceCH2ParamTypeId; + m_attachedDeviceParamTypeIdMap[sonoff_quadDeviceClassId] << sonoff_quadDeviceAttachedDeviceCH1ParamTypeId << sonoff_quadDeviceAttachedDeviceCH2ParamTypeId << sonoff_quadDeviceAttachedDeviceCH3ParamTypeId << sonoff_quadDeviceAttachedDeviceCH4ParamTypeId; + + // Helper maps for virtual childs (aka tasmota*) + m_channelParamTypeMap[tasmotaSwitchDeviceClassId] = tasmotaSwitchDeviceChannelNameParamTypeId; + m_channelParamTypeMap[tasmotaLightDeviceClassId] = tasmotaLightDeviceChannelNameParamTypeId; + + m_powerStateTypeMap[tasmotaSwitchDeviceClassId] = tasmotaSwitchPowerStateTypeId; + m_powerStateTypeMap[tasmotaLightDeviceClassId] = tasmotaLightPowerStateTypeId; + + // Helper maps for all devices + m_connectedStateTypeMap[sonoff_basicDeviceClassId] = sonoff_basicConnectedStateTypeId; + m_connectedStateTypeMap[sonoff_dualDeviceClassId] = sonoff_dualConnectedStateTypeId; + m_connectedStateTypeMap[sonoff_quadDeviceClassId] = sonoff_quadConnectedStateTypeId; + m_connectedStateTypeMap[tasmotaSwitchDeviceClassId] = tasmotaSwitchConnectedStateTypeId; + m_connectedStateTypeMap[tasmotaLightDeviceClassId] = tasmotaLightConnectedStateTypeId; +} + +DevicePluginTasmota::~DevicePluginTasmota() +{ +} + +void DevicePluginTasmota::init() +{ +} + +DeviceManager::DeviceSetupStatus DevicePluginTasmota::setupDevice(Device *device) +{ + if (m_ipAddressParamTypeMap.contains(device->deviceClassId())) { + ParamTypeId ipAddressParamTypeId = m_ipAddressParamTypeMap.value(device->deviceClassId()); + + QHostAddress deviceAddress = QHostAddress(device->paramValue(ipAddressParamTypeId).toString()); + if (deviceAddress.isNull()) { + qCWarning(dcTasmota) << "Not a valid IP address given for IP address parameter"; + return DeviceManager::DeviceSetupStatusFailure; + } + MqttChannel *channel = hardwareManager()->mqttProvider()->createChannel(device->id(), deviceAddress); + + Q_UNUSED(device) + QUrl url("http://10.10.10.90/sv"); + QUrlQuery query; + query.addQueryItem("w", "2%2C1"); + query.addQueryItem("mh", channel->serverAddress().toString()); + query.addQueryItem("ml", QString::number(channel->serverPort())); + query.addQueryItem("mc", channel->clientId()); + query.addQueryItem("mu", channel->username()); + query.addQueryItem("mp", channel->password()); + query.addQueryItem("mt", "sonoff"); + query.addQueryItem("mf", channel->topicPrefix() + "/%topic%/"); + url.setQuery(query); + QNetworkRequest request(url); + QNetworkReply *reply = hardwareManager()->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, this, [this, device, channel, reply](){ + reply->deleteLater(); + if (reply->error() != QNetworkReply::NoError) { + qCDebug(dcTasmota) << "Sonoff device setup call failed:" << reply->error() << reply->errorString() << reply->readAll(); + hardwareManager()->mqttProvider()->releaseChannel(channel); + emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure); + return; + } + m_mqttChannels.insert(device, channel); + connect(channel, &MqttChannel::clientConnected, this, &DevicePluginTasmota::onClientConnected); + connect(channel, &MqttChannel::clientDisconnected, this, &DevicePluginTasmota::onClientDisconnected); + connect(channel, &MqttChannel::publishReceived, this, &DevicePluginTasmota::onPublishReceived); + + qCDebug(dcTasmota) << "Sonoff setup complete"; + emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess); + + foreach (Device *child, myDevices()) { + if (child->parentId() == device->id()) { + // Already have child devices... We're done here + return; + } + } + qCDebug(dcTasmota) << "Adding Tasmota Switch devices"; + QList deviceDescriptors; + for (int i = 0; i < m_attachedDeviceParamTypeIdMap.value(device->deviceClassId()).count(); i++) { + DeviceDescriptor descriptor(tasmotaSwitchDeviceClassId, device->name() + " CH" + QString::number(i+1), QString(), device->id()); + if (m_attachedDeviceParamTypeIdMap.value(device->deviceClassId()).count() == 1) { + descriptor.setParams(ParamList() << Param(tasmotaSwitchDeviceChannelNameParamTypeId, "POWER")); + } else { + descriptor.setParams(ParamList() << Param(tasmotaSwitchDeviceChannelNameParamTypeId, "POWER" + QString::number(i+1))); + } + deviceDescriptors << descriptor; + } + emit autoDevicesAppeared(tasmotaSwitchDeviceClassId, deviceDescriptors); + + qCDebug(dcTasmota) << "Adding Tasmota connected devices"; + deviceDescriptors.clear(); + for (int i = 0; i < m_attachedDeviceParamTypeIdMap.value(device->deviceClassId()).count(); i++) { + ParamTypeId attachedDeviceParamTypeId = m_attachedDeviceParamTypeIdMap.value(device->deviceClassId()).at(i); + if (device->paramValue(attachedDeviceParamTypeId).toString() == "Light") { + DeviceDescriptor descriptor1(tasmotaLightDeviceClassId, device->name() + " CH" + QString::number(i+1), QString(), device->id()); + if (m_attachedDeviceParamTypeIdMap.value(device->deviceClassId()).count() == 1) { + descriptor1.setParams(ParamList() << Param(tasmotaLightDeviceChannelNameParamTypeId, "POWER")); + } else { + descriptor1.setParams(ParamList() << Param(tasmotaLightDeviceChannelNameParamTypeId, "POWER" + QString::number(i+1))); + } + deviceDescriptors << descriptor1; + } + } + if (!deviceDescriptors.isEmpty()) { + emit autoDevicesAppeared(tasmotaLightDeviceClassId, deviceDescriptors); + } + }); + return DeviceManager::DeviceSetupStatusAsync; + } + + if (m_connectedStateTypeMap.contains(device->deviceClassId())) { + Device* parentDevice = myDevices().findById(device->parentId()); + StateTypeId connectedStateTypeId = m_connectedStateTypeMap.value(device->deviceClassId()); + device->setStateValue(m_connectedStateTypeMap.value(device->deviceClassId()), parentDevice->stateValue(connectedStateTypeId)); + return DeviceManager::DeviceSetupStatusSuccess; + } + + qCWarning(dcTasmota) << "Unhandled DeviceClass in setupDevice" << device->deviceClassId(); + return DeviceManager::DeviceSetupStatusFailure; +} + +void DevicePluginTasmota::deviceRemoved(Device *device) +{ + qCDebug(dcTasmota) << "Device removed" << device->name(); + if (m_mqttChannels.contains(device)) { + qCDebug(dcTasmota) << "Releasing MQTT channel"; + MqttChannel* channel = m_mqttChannels.take(device); + hardwareManager()->mqttProvider()->releaseChannel(channel); + } +} + +DeviceManager::DeviceError DevicePluginTasmota::executeAction(Device *device, const Action &action) +{ + if (m_powerStateTypeMap.contains(device->deviceClassId())) { + Device *parentDev = myDevices().findById(device->parentId()); + MqttChannel *channel = m_mqttChannels.value(parentDev); + ParamTypeId channelParamTypeId = m_channelParamTypeMap.value(device->deviceClassId()); + ParamTypeId powerActionParamTypeId = ParamTypeId(m_powerStateTypeMap.value(device->deviceClassId()).toString()); + qCDebug(dcTasmota) << "Publishing:" << channel->topicPrefix() + "/sonoff/cmnd/" + device->paramValue(channelParamTypeId).toString() << (action.param(powerActionParamTypeId).value().toBool() ? "ON" : "OFF"); + channel->publish(channel->topicPrefix() + "/sonoff/cmnd/" + device->paramValue(channelParamTypeId).toString().toLower(), action.param(powerActionParamTypeId).value().toBool() ? "ON" : "OFF"); + } + return DeviceManager::DeviceErrorActionTypeNotFound; +} + +void DevicePluginTasmota::onClientConnected(MqttChannel *channel) +{ + qCDebug(dcTasmota) << "Sonoff device connected!"; + Device *dev = m_mqttChannels.key(channel); + dev->setStateValue(m_connectedStateTypeMap.value(dev->deviceClassId()), true); + + foreach (Device *child, myDevices()) { + if (child->parentId() == dev->id()) { + child->setStateValue(m_connectedStateTypeMap.value(child->deviceClassId()), true); + } + } +} + +void DevicePluginTasmota::onClientDisconnected(MqttChannel *channel) +{ + qCDebug(dcTasmota) << "Sonoff device disconnected!"; + Device *dev = m_mqttChannels.key(channel); + dev->setStateValue(m_connectedStateTypeMap.value(dev->deviceClassId()), false); + + foreach (Device *child, myDevices()) { + if (child->parentId() == dev->id()) { + child->setStateValue(m_connectedStateTypeMap.value(dev->deviceClassId()), false); + } + } +} + +void DevicePluginTasmota::onPublishReceived(MqttChannel *channel, const QString &topic, const QByteArray &payload) +{ + qCDebug(dcTasmota) << "Publish received from Sonoff device:" << topic << payload; + Device *dev = m_mqttChannels.key(channel); + if (m_ipAddressParamTypeMap.contains(dev->deviceClassId())) { + if (!topic.startsWith(channel->topicPrefix() + "/sonoff/POWER")) { + return; + } + QString channelName = topic.split("/").last(); + + foreach (Device *child, myDevices()) { + if (child->parentId() != dev->id()) { + continue; + } + if (child->paramValue(m_channelParamTypeMap.value(child->deviceClassId())).toString() != channelName) { + continue; + } + child->setStateValue(m_powerStateTypeMap.value(child->deviceClassId()), payload == "ON"); + } + } +} + diff --git a/tasmota/deviceplugintasmota.h b/tasmota/deviceplugintasmota.h new file mode 100644 index 00000000..f73bde9b --- /dev/null +++ b/tasmota/deviceplugintasmota.h @@ -0,0 +1,66 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Michael Zanetti * + * * + * 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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DEVICEPLUGINTASMOTA_H +#define DEVICEPLUGINTASMOTA_H + +#include "plugin/deviceplugin.h" +#include "devicemanager.h" + +class MqttChannel; + +class DevicePluginTasmota: public DevicePlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "deviceplugintasmota.json") + Q_INTERFACES(DevicePlugin) + + +public: + explicit DevicePluginTasmota(); + ~DevicePluginTasmota(); + + void init() override; + DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; + void deviceRemoved(Device *device) override; + DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; + +private slots: + void onClientConnected(MqttChannel *channel); + void onClientDisconnected(MqttChannel *channel); + void onPublishReceived(MqttChannel *channel, const QString &topic, const QByteArray &payload); + +private: + QHash m_mqttChannels; + + // Helpers for parent devices (the ones starting with sonoff) + QHash m_ipAddressParamTypeMap; + QHash > m_attachedDeviceParamTypeIdMap; + + // Helpers for child devices (virtual ones, starting with tasmota) + QHash m_channelParamTypeMap; + QHash m_powerStateTypeMap; + + // Helpers for both devices + QHash m_connectedStateTypeMap; +}; + +#endif // DEVICEPLUGINTASMOTA_H diff --git a/tasmota/deviceplugintasmota.json b/tasmota/deviceplugintasmota.json new file mode 100644 index 00000000..7709ccb3 --- /dev/null +++ b/tasmota/deviceplugintasmota.json @@ -0,0 +1,220 @@ +{ + "name": "tasmota", + "displayName": "Sonoff-Tasmota", + "id": "d136e0c0-0cbf-4731-aabb-b2201088d6cb", + "vendors": [ + { + "name": "tasmota", + "displayName": "Sonoff-Tasmota", + "id": "789e6173-3de3-44ef-8664-9492c9d15d44", + "deviceClasses": [ + { + "id": "f39fdfa8-73e0-4cf4-8d05-dc237ced7a57", + "name": "sonoff_basic", + "displayName": "Sonoff switch (Basic, RF, Touch...)", + "createMethods": ["user"], + "interfaces": [ "gateway" ], + "paramTypes": [ + { + "id": "cdead654-a765-488c-9fe6-ce6afb550d8b", + "name":"ipAddress", + "displayName": "IP address", + "type": "QString" + }, + { + "id": "f210d0c0-dda1-442d-a0cc-2f2e48c24984", + "name": "attachedDeviceCH1", + "displayName": "Connected device", + "type": "QString", + "allowedValues": ["None", "Light"], + "defaultValue": "None" + } + ], + "stateTypes": [ + { + "id": "9cde6321-2abf-4a58-a1d6-c7418edb9747", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false, + "cached": false + } + ] + }, + { + "id": "425ab191-833c-4618-8ac8-aff02370b99d", + "name": "sonoff_dual", + "displayName": "Sonoff dual switch (Dual, T1 2CH...)", + "createMethods": ["user"], + "interfaces": [ "gateway" ], + "paramTypes": [ + { + "id": "7fe081a4-b9ec-4ca5-b583-50e992a24f4d", + "name":"ipAddress", + "displayName": "IP address", + "type": "QString" + }, + { + "id": "5101ad0d-c887-44b8-998d-021948184ccd", + "name": "attachedDeviceCH1", + "displayName": "Connected device", + "type": "QString", + "allowedValues": ["None", "Light"], + "defaultValue": "None" + }, + { + "id": "530edfb0-930d-4885-b1c0-3bf51a7671f1", + "name": "attachedDeviceCH2", + "displayName": "Connected device", + "type": "QString", + "allowedValues": ["None", "Light"], + "defaultValue": "None" + } + ], + "stateTypes": [ + { + "id": "e2f55332-e706-412e-beb6-abf76b3bcff3", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false, + "cached": false + } + ] + }, + { + "id": "ae845ec9-be61-4bdf-9015-4c156f937da7", + "name": "sonoff_quad", + "displayName": "Sonoff 4 channel switch (4CH, T1 4CH...)", + "createMethods": ["user"], + "interfaces": [ "gateway" ], + "paramTypes": [ + { + "id": "dbc3f3b3-2d17-40e9-8f6e-dde0b26952bc", + "name":"ipAddress", + "displayName": "IP address", + "type": "QString" + }, + { + "id": "d6520a7a-d340-4f42-a8c4-b2da8434f40f", + "name": "attachedDeviceCH1", + "displayName": "Connected device 1", + "type": "QString", + "allowedValues": ["None", "Light"], + "defaultValue": "None" + }, + { + "id": "b27a1ad0-4455-4264-81a1-d625e312c330", + "name": "attachedDeviceCH2", + "displayName": "Connected device 2", + "type": "QString", + "allowedValues": ["None", "Light"], + "defaultValue": "None" + }, + { + "id": "ee2509fb-7690-47f7-af74-05635b460be7", + "name": "attachedDeviceCH3", + "displayName": "Connected device 3", + "type": "QString", + "allowedValues": ["None", "Light"], + "defaultValue": "None" + }, + { + "id": "a5bdb44f-9789-4e0a-8274-ec7866e8f148", + "name": "attachedDeviceCH4", + "displayName": "Connected device 4", + "type": "QString", + "allowedValues": ["None", "Light"], + "defaultValue": "None" + } + ], + "stateTypes": [ + { + "id": "5b422d28-9f60-4ea9-ab23-42a0ec605b9e", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false, + "cached": false + } + ] + }, + { + "id": "8a5e69c0-14ad-4ae8-9ff9-10055de6ffdf", + "name": "tasmotaSwitch", + "displayName": "Tasmota power switch", + "createMethods": ["auto"], + "interfaces": ["power", "connectable"], + "paramTypes": [ + { + "id": "564cf6c6-86eb-41a5-9b87-fb32f1b6fcd6", + "name": "channelName", + "displayName": "Channel name", + "type": "QString" + } + ], + "stateTypes": [ + { + "id": "b4607e5d-70c4-4e76-9d9a-c6de7c50377e", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false, + "cached": false + }, + { + "id": "413503d7-fc9f-417a-95fa-5c350a6f69f9", + "name": "power", + "displayName": "Power", + "displayNameEvent": "Power changed", + "displayNameAction": "Set power", + "type": "bool", + "defaultValue": false, + "writable": true + } + ] + }, + { + "id": "83e5d9e6-5ac8-4e41-9717-481415048d49", + "name": "tasmotaLight", + "displayName": "Tasmota light", + "createMethods": ["auto"], + "interfaces": ["light", "connectable"], + "paramTypes": [ + { + "id": "1f792ae4-cf39-4e12-99ca-c593bd020fcb", + "name": "channelName", + "displayName": "Channel name", + "type": "QString" + } + ], + "stateTypes": [ + { + "id": "72050de9-c318-4e53-93e5-36f7c2fc7cab", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false, + "cached": false + }, + { + "id": "88dbdf8e-45ff-466f-8352-8654a6b5fe68", + "name": "power", + "displayName": "Power", + "displayNameEvent": "Power changed", + "displayNameAction": "Set power", + "type": "bool", + "defaultValue": false, + "writable": true + } + ] + } + ] + } + ] +} diff --git a/tasmota/tasmota.pro b/tasmota/tasmota.pro new file mode 100644 index 00000000..c70f70f7 --- /dev/null +++ b/tasmota/tasmota.pro @@ -0,0 +1,11 @@ +include(../plugins.pri) + +QT += network + +TARGET = $$qtLibraryTarget(nymea_deviceplugintasmota) + +SOURCES += \ + deviceplugintasmota.cpp \ + +HEADERS += \ + deviceplugintasmota.h \ diff --git a/tasmota/translations/d136e0c0-0cbf-4731-aabb-b2201088d6cb-en_US.ts b/tasmota/translations/d136e0c0-0cbf-4731-aabb-b2201088d6cb-en_US.ts new file mode 100644 index 00000000..f7f66d85 --- /dev/null +++ b/tasmota/translations/d136e0c0-0cbf-4731-aabb-b2201088d6cb-en_US.ts @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From fe0530c02b896c1027d0e9cef7f598eb47e41d82 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 27 Nov 2018 12:07:17 +0100 Subject: [PATCH 02/16] improve messages on device setup failure --- tasmota/deviceplugintasmota.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tasmota/deviceplugintasmota.cpp b/tasmota/deviceplugintasmota.cpp index c684e8aa..09efbb21 100644 --- a/tasmota/deviceplugintasmota.cpp +++ b/tasmota/deviceplugintasmota.cpp @@ -76,8 +76,11 @@ DeviceManager::DeviceSetupStatus DevicePluginTasmota::setupDevice(Device *device return DeviceManager::DeviceSetupStatusFailure; } MqttChannel *channel = hardwareManager()->mqttProvider()->createChannel(device->id(), deviceAddress); + if (!channel) { + qCWarning(dcTasmota) << "Failed to create MQTT channel."; + return DeviceManager::DeviceSetupStatusFailure; + } - Q_UNUSED(device) QUrl url("http://10.10.10.90/sv"); QUrlQuery query; query.addQueryItem("w", "2%2C1"); From c08836a7494e79271a38d03eb3cfc9de9403f887 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 28 Nov 2018 12:42:13 +0100 Subject: [PATCH 03/16] document the tasmota plugin --- tasmota/deviceplugintasmota.cpp | 52 ++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/tasmota/deviceplugintasmota.cpp b/tasmota/deviceplugintasmota.cpp index 09efbb21..ee66a983 100644 --- a/tasmota/deviceplugintasmota.cpp +++ b/tasmota/deviceplugintasmota.cpp @@ -4,20 +4,56 @@ * * * 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. * + * 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. * * * - * nymea is distributed in the hope that it will be useful, * + * 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 General Public License for more details. * + * 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 General Public License * - * along with nymea. If not, see . * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/*! + \page tasmota.html + \title Tasmota (Sonoff) devices + \brief Plugin for Tasmota devices (Sonoff devices flashed with Tasmota and others) + + \ingroup plugins + \ingroup nymea-plugins-maker + + This plugin allows to make use of Sonoff-Tasmota devices via the nymea internal MQTT broker. + There is no external MQTT broker needed. + + Note that Sonoff devices must be flashed with the Tasmota sofware and connected to the WiFi network in order to + work with this plugin. + + See https://github.com/arendst/Sonoff-Tasmota/wiki for a list of all supported devices and instructions on how to + install Tasmota on those. + + After flashing Tasmota to a Sonoff device and connecting it to WiFi, it can be added to nymea. The only required + thing is the IP address to the device. This plugin will create a new isoloated MQTT channel on the nymea internal + MQTT broker and provision login details to the Tasmota device via HTTP. Once that is successful, the Tasmota device + will connect to the MQTT broker and appear as connected in nymea. + + \chapter Plugin properties + When adding a Tasmota device it will add a new Gateway type device representing the Tasmota device itself. In addition + to that a power switch device will appear which can be used to control the switches in the Tasmota device. Upon + device setup, the user can optionally select the type of the connected hardware, (e.g. a light) which causes this + plugin to create a light device in the system which also controls the switches inside the Tasmota device and nicely + integrates with the nymea:ux for the given device type. + + For more details how to read this JSON file please check out the documentation for \l{The plugin JSON File}. + + \quotefile plugins/deviceplugins/tasmota/deviceplugintasmota.json +*/ + #include "deviceplugintasmota.h" #include "plugininfo.h" From 7827c198a07f086a18fab8761cefca3c457a0eed Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 28 Nov 2018 23:08:05 +0100 Subject: [PATCH 04/16] add tasmota plugin to debian install files --- debian/control | 15 +++++++++++++++ debian/nymea-plugin-tasmota.install.in | 1 + 2 files changed, 16 insertions(+) create mode 100644 debian/nymea-plugin-tasmota.install.in diff --git a/debian/control b/debian/control index f08db740..c5fc0465 100644 --- a/debian/control +++ b/debian/control @@ -429,6 +429,21 @@ Description: nymea.io plugin for pushbullet This package will install the nymea.io plugin for pushbullet +Package: nymea-plugin-tasmota +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + nymea-plugins-translations, +Description: nymea.io plugin for Sonoff-Tasmota devices + The nymea daemon is a plugin based IoT (Internet of Things) server. The + server works like a translator for devices, things and services and + allows them to interact. + With the powerful rule engine you are able to connect any device available + in the system and create individual scenes and behaviors for your environment. + . + This package will install the nymea.io plugin for Tasmota-Sonoff devices + + Package: nymea-plugin-udpcommander Architecture: any Depends: ${shlibs:Depends}, diff --git a/debian/nymea-plugin-tasmota.install.in b/debian/nymea-plugin-tasmota.install.in new file mode 100644 index 00000000..5bd78e4a --- /dev/null +++ b/debian/nymea-plugin-tasmota.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_deviceplugintasmota.so From 9a51610836dae2a3d2fedfb306bee8f48b48c88e Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 29 Nov 2018 13:59:07 +0100 Subject: [PATCH 05/16] add tasmota plugin to generic plugin package --- debian/control | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/control b/debian/control index c5fc0465..f836b4cf 100644 --- a/debian/control +++ b/debian/control @@ -699,6 +699,7 @@ Depends: nymea-plugin-awattar, nymea-plugin-pushbullet, nymea-plugin-usbwde, nymea-plugin-wakeonlan, + nymea-plugin-tasmota, nymea-plugin-wemo, nymea-plugin-elgato, nymea-plugin-senic, From 5d60b161e97c0427a819c1c60f5e7bc47c101a83 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 1 Dec 2018 01:05:04 +0100 Subject: [PATCH 06/16] don't wait for confirmation, immediately set state value --- tasmota/deviceplugintasmota.cpp | 40 ++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/tasmota/deviceplugintasmota.cpp b/tasmota/deviceplugintasmota.cpp index ee66a983..34d44dfb 100644 --- a/tasmota/deviceplugintasmota.cpp +++ b/tasmota/deviceplugintasmota.cpp @@ -216,8 +216,10 @@ DeviceManager::DeviceError DevicePluginTasmota::executeAction(Device *device, co ParamTypeId powerActionParamTypeId = ParamTypeId(m_powerStateTypeMap.value(device->deviceClassId()).toString()); qCDebug(dcTasmota) << "Publishing:" << channel->topicPrefix() + "/sonoff/cmnd/" + device->paramValue(channelParamTypeId).toString() << (action.param(powerActionParamTypeId).value().toBool() ? "ON" : "OFF"); channel->publish(channel->topicPrefix() + "/sonoff/cmnd/" + device->paramValue(channelParamTypeId).toString().toLower(), action.param(powerActionParamTypeId).value().toBool() ? "ON" : "OFF"); + device->setStateValue(m_powerStateTypeMap.value(device->deviceClassId()), action.param(powerActionParamTypeId).value().toBool()); + return DeviceManager::DeviceErrorNoError; } - return DeviceManager::DeviceErrorActionTypeNotFound; + return DeviceManager::DeviceErrorDeviceClassNotFound; } void DevicePluginTasmota::onClientConnected(MqttChannel *channel) @@ -251,19 +253,35 @@ void DevicePluginTasmota::onPublishReceived(MqttChannel *channel, const QString qCDebug(dcTasmota) << "Publish received from Sonoff device:" << topic << payload; Device *dev = m_mqttChannels.key(channel); if (m_ipAddressParamTypeMap.contains(dev->deviceClassId())) { - if (!topic.startsWith(channel->topicPrefix() + "/sonoff/POWER")) { - return; - } - QString channelName = topic.split("/").last(); + if (topic.startsWith(channel->topicPrefix() + "/sonoff/POWER")) { + QString channelName = topic.split("/").last(); - foreach (Device *child, myDevices()) { - if (child->parentId() != dev->id()) { - continue; + foreach (Device *child, myDevices()) { + if (child->parentId() != dev->id()) { + continue; + } + if (child->paramValue(m_channelParamTypeMap.value(child->deviceClassId())).toString() != channelName) { + continue; + } + child->setStateValue(m_powerStateTypeMap.value(child->deviceClassId()), payload == "ON"); } - if (child->paramValue(m_channelParamTypeMap.value(child->deviceClassId())).toString() != channelName) { - continue; + } + if (topic.startsWith(channel->topicPrefix() + "/somoff/STATE")) { + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcTasmota) << "Cannot parse JSON from Tasmota device" << error.errorString(); + return; } - child->setStateValue(m_powerStateTypeMap.value(child->deviceClassId()), payload == "ON"); + foreach (Device *child, myDevices()) { + if (child->parentId() != dev->id()) { + continue; + } + QString childChannel = child->paramValue(m_channelParamTypeMap.value(child->deviceClassId())).toString(); + QString valueString = jsonDoc.toVariant().toMap().value(childChannel).toString(); + child->setStateValue(m_powerStateTypeMap.value(child->deviceClassId()), valueString == "ON"); + } + } } } From a3b85b19c44e2ba0b5bcd3f20dfb5331e39c1614 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 9 Dec 2018 18:31:02 +0100 Subject: [PATCH 07/16] properly set device parent in tasmota child devices --- tasmota/deviceplugintasmota.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tasmota/deviceplugintasmota.cpp b/tasmota/deviceplugintasmota.cpp index 34d44dfb..bc0e5479 100644 --- a/tasmota/deviceplugintasmota.cpp +++ b/tasmota/deviceplugintasmota.cpp @@ -170,13 +170,14 @@ DeviceManager::DeviceSetupStatus DevicePluginTasmota::setupDevice(Device *device for (int i = 0; i < m_attachedDeviceParamTypeIdMap.value(device->deviceClassId()).count(); i++) { ParamTypeId attachedDeviceParamTypeId = m_attachedDeviceParamTypeIdMap.value(device->deviceClassId()).at(i); if (device->paramValue(attachedDeviceParamTypeId).toString() == "Light") { - DeviceDescriptor descriptor1(tasmotaLightDeviceClassId, device->name() + " CH" + QString::number(i+1), QString(), device->id()); + DeviceDescriptor descriptor(tasmotaLightDeviceClassId, device->name() + " CH" + QString::number(i+1), QString(), device->id()); + descriptor.setParentDeviceId(device->id()); if (m_attachedDeviceParamTypeIdMap.value(device->deviceClassId()).count() == 1) { - descriptor1.setParams(ParamList() << Param(tasmotaLightDeviceChannelNameParamTypeId, "POWER")); + descriptor.setParams(ParamList() << Param(tasmotaLightDeviceChannelNameParamTypeId, "POWER")); } else { - descriptor1.setParams(ParamList() << Param(tasmotaLightDeviceChannelNameParamTypeId, "POWER" + QString::number(i+1))); + descriptor.setParams(ParamList() << Param(tasmotaLightDeviceChannelNameParamTypeId, "POWER" + QString::number(i+1))); } - deviceDescriptors << descriptor1; + deviceDescriptors << descriptor; } } if (!deviceDescriptors.isEmpty()) { From 5229e56c31d389c7a19ecf0fd71346454ddc0b2e Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 9 Dec 2018 18:53:26 +0100 Subject: [PATCH 08/16] small fixes --- tasmota/deviceplugintasmota.cpp | 4 ++-- tasmota/deviceplugintasmota.json | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tasmota/deviceplugintasmota.cpp b/tasmota/deviceplugintasmota.cpp index bc0e5479..6d8a9a6b 100644 --- a/tasmota/deviceplugintasmota.cpp +++ b/tasmota/deviceplugintasmota.cpp @@ -244,7 +244,7 @@ void DevicePluginTasmota::onClientDisconnected(MqttChannel *channel) foreach (Device *child, myDevices()) { if (child->parentId() == dev->id()) { - child->setStateValue(m_connectedStateTypeMap.value(dev->deviceClassId()), false); + child->setStateValue(m_connectedStateTypeMap.value(child->deviceClassId()), false); } } } @@ -267,7 +267,7 @@ void DevicePluginTasmota::onPublishReceived(MqttChannel *channel, const QString child->setStateValue(m_powerStateTypeMap.value(child->deviceClassId()), payload == "ON"); } } - if (topic.startsWith(channel->topicPrefix() + "/somoff/STATE")) { + if (topic.startsWith(channel->topicPrefix() + "/sonoff/STATE")) { QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &error); if (error.error != QJsonParseError::NoError) { diff --git a/tasmota/deviceplugintasmota.json b/tasmota/deviceplugintasmota.json index 7709ccb3..99c343b0 100644 --- a/tasmota/deviceplugintasmota.json +++ b/tasmota/deviceplugintasmota.json @@ -11,7 +11,7 @@ { "id": "f39fdfa8-73e0-4cf4-8d05-dc237ced7a57", "name": "sonoff_basic", - "displayName": "Sonoff switch (Basic, RF, Touch...)", + "displayName": "Single switch (Sonoff Basic, RF, Touch...)", "createMethods": ["user"], "interfaces": [ "gateway" ], "paramTypes": [ @@ -45,7 +45,7 @@ { "id": "425ab191-833c-4618-8ac8-aff02370b99d", "name": "sonoff_dual", - "displayName": "Sonoff dual switch (Dual, T1 2CH...)", + "displayName": "Dual switch (Sonoff Dual, T1 2CH...)", "createMethods": ["user"], "interfaces": [ "gateway" ], "paramTypes": [ @@ -58,7 +58,7 @@ { "id": "5101ad0d-c887-44b8-998d-021948184ccd", "name": "attachedDeviceCH1", - "displayName": "Connected device", + "displayName": "Connected device 1", "type": "QString", "allowedValues": ["None", "Light"], "defaultValue": "None" @@ -66,7 +66,7 @@ { "id": "530edfb0-930d-4885-b1c0-3bf51a7671f1", "name": "attachedDeviceCH2", - "displayName": "Connected device", + "displayName": "Connected device 2", "type": "QString", "allowedValues": ["None", "Light"], "defaultValue": "None" @@ -87,7 +87,7 @@ { "id": "ae845ec9-be61-4bdf-9015-4c156f937da7", "name": "sonoff_quad", - "displayName": "Sonoff 4 channel switch (4CH, T1 4CH...)", + "displayName": "4 channel switch (Sonoff 4CH, T1 4CH...)", "createMethods": ["user"], "interfaces": [ "gateway" ], "paramTypes": [ From 3d8988a0a98925d19b3d478b204b2f2e3cc8b887 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 27 Nov 2018 23:30:09 +0100 Subject: [PATCH 09/16] add a generic MQTT client plugin --- mqtt/devicepluginmqtt.cpp | 195 +++++++++++++++++ mqtt/devicepluginmqtt.h | 61 ++++++ mqtt/devicepluginmqtt.json | 196 ++++++++++++++++++ mqtt/mqtt.pro | 13 ++ ...58205-07c8-4482-85ad-b435387803a5-en_US.ts | 4 + nymea-plugins.pro | 1 + 6 files changed, 470 insertions(+) create mode 100644 mqtt/devicepluginmqtt.cpp create mode 100644 mqtt/devicepluginmqtt.h create mode 100644 mqtt/devicepluginmqtt.json create mode 100644 mqtt/mqtt.pro create mode 100644 mqtt/translations/27c58205-07c8-4482-85ad-b435387803a5-en_US.ts diff --git a/mqtt/devicepluginmqtt.cpp b/mqtt/devicepluginmqtt.cpp new file mode 100644 index 00000000..34359967 --- /dev/null +++ b/mqtt/devicepluginmqtt.cpp @@ -0,0 +1,195 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Michael Zanetti * + * * + * 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 * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*! + \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(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(); +} + diff --git a/mqtt/devicepluginmqtt.h b/mqtt/devicepluginmqtt.h new file mode 100644 index 00000000..0289e4a1 --- /dev/null +++ b/mqtt/devicepluginmqtt.h @@ -0,0 +1,61 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Michael Zanetti * + * * + * 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 * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DEVICEPLUGINMQTT_H +#define DEVICEPLUGINMQTT_H + +#include "plugin/deviceplugin.h" + +#include +#include +#include + +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 m_clients; + + QHash m_pendingPublishes; +}; + +#endif // DEVICEPLUGINMQTT_H diff --git a/mqtt/devicepluginmqtt.json b/mqtt/devicepluginmqtt.json new file mode 100644 index 00000000..5a19379d --- /dev/null +++ b/mqtt/devicepluginmqtt.json @@ -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 + } + ] + } + ] + } + ] + } + ] +} diff --git a/mqtt/mqtt.pro b/mqtt/mqtt.pro new file mode 100644 index 00000000..6a97ecc0 --- /dev/null +++ b/mqtt/mqtt.pro @@ -0,0 +1,13 @@ +include(../plugins.pri) + +QT += network + +TARGET = $$qtLibraryTarget(nymea_devicepluginmqtt) + +SOURCES += \ + devicepluginmqtt.cpp + +HEADERS += \ + devicepluginmqtt.h + + diff --git a/mqtt/translations/27c58205-07c8-4482-85ad-b435387803a5-en_US.ts b/mqtt/translations/27c58205-07c8-4482-85ad-b435387803a5-en_US.ts new file mode 100644 index 00000000..f7f66d85 --- /dev/null +++ b/mqtt/translations/27c58205-07c8-4482-85ad-b435387803a5-en_US.ts @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/nymea-plugins.pro b/nymea-plugins.pro index 2afdb106..5840818f 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -22,6 +22,7 @@ PLUGIN_DIRS = \ lgsmarttv \ mailnotification \ multisensor \ + mqtt \ netatmo \ networkdetector \ openweathermap \ From 9729f67b85aaa687b73e8cb388433815562ed5f9 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 28 Nov 2018 12:20:58 +0100 Subject: [PATCH 10/16] update after api change in mqtt client --- mqtt/devicepluginmqtt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mqtt/devicepluginmqtt.cpp b/mqtt/devicepluginmqtt.cpp index 34359967..d9f50236 100644 --- a/mqtt/devicepluginmqtt.cpp +++ b/mqtt/devicepluginmqtt.cpp @@ -86,7 +86,7 @@ DeviceManager::DeviceSetupStatus DevicePluginMqtt::setupDevice(Device *device) connect(client, &MqttClient::connected, this, [this, device](){ subscribe(device); }); - connect(client, &MqttClient::subscribed, this, [this, device](quint16 packetId, const Mqtt::SubscribeReturnCodes returnCodes){ + connect(client, &MqttClient::subscribeResult, this, [this, device](quint16 packetId, const Mqtt::SubscribeReturnCodes returnCodes){ Q_UNUSED(packetId) emit deviceSetupFinished(device, returnCodes.first() == Mqtt::SubscribeReturnCodeFailure ? DeviceManager::DeviceSetupStatusFailure : DeviceManager::DeviceSetupStatusSuccess); }); From 949564bbdf31db573cac3ba8bfc7b148bd3426fb Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 28 Nov 2018 12:32:50 +0100 Subject: [PATCH 11/16] document the MQTT plugin --- mqtt/devicepluginmqtt.cpp | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/mqtt/devicepluginmqtt.cpp b/mqtt/devicepluginmqtt.cpp index d9f50236..69b7b638 100644 --- a/mqtt/devicepluginmqtt.cpp +++ b/mqtt/devicepluginmqtt.cpp @@ -23,39 +23,32 @@ /*! \page mqtt.html \title Generic MQTT - \brief Plugin for catching UDP commands from the network. + \brief Plugin for subscribing and publishing to an MQTT broker. \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. + This plugin allows to subscribe and publish to MQTT brokers (the nymea internal broker and external ones). \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}. + A device is configured to publish its state to a MQTT broker. Using this plugin the user can subscribe to + the same topic on that broker and monitor the device's state. - \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. + Publishing is also supported. This allows use cases such as controlling IoT things via MQTT by publishing + to topics such devices are subscribed to. Other possibilities are to use nymea as a "translator" between other + transport layers to MQTT. For instance a sensor might deliver sensor data via Bluetooth to Nymea and using this + MQTT plugin and a \l{nymeaserver::Rule} nymea can be configured to forward all those sensor values to a MQTT broker. \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}. + The plugin implements the inputtrigger and outputtrigger interfaces, adding parameters for topic and payload to + the trigger action and triggered event. 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 + \quotefile plugins/deviceplugins/mqtt/devicepluginmqtt.json */ #include "devicepluginmqtt.h" From 4f0a4267b2cbc20a675ddc5f1219276d74af68f3 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 28 Nov 2018 22:37:17 +0100 Subject: [PATCH 12/16] add build depends on libnymea-mqtt-dev --- debian/control | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/control b/debian/control index f836b4cf..0d299087 100644 --- a/debian/control +++ b/debian/control @@ -4,6 +4,7 @@ Priority: options Maintainer: Michael Zanetti Build-depends: debhelper (>= 0.0.0), libnymea1-dev, + libnymea-mqtt-dev, libqt5serialport5-dev, python:any, qtbase5-dev, From 09c73ef484059c2ca128582fcaa30fa7361efc51 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 28 Nov 2018 23:16:42 +0100 Subject: [PATCH 13/16] add mqtt client plugin to debian files and rename it --- debian/control | 16 ++++++++++++++ debian/nymea-plugin-mqttclient.install.in | 1 + mqtt/mqtt.pro | 13 ----------- .../devicepluginmqttclient.cpp | 22 +++++++++---------- .../devicepluginmqttclient.h | 12 +++++----- .../devicepluginmqttclient.json | 0 mqttclient/mqttclient.pro | 13 +++++++++++ ...58205-07c8-4482-85ad-b435387803a5-en_US.ts | 0 nymea-plugins.pro | 2 +- 9 files changed, 48 insertions(+), 31 deletions(-) create mode 100644 debian/nymea-plugin-mqttclient.install.in delete mode 100644 mqtt/mqtt.pro rename mqtt/devicepluginmqtt.cpp => mqttclient/devicepluginmqttclient.cpp (92%) rename mqtt/devicepluginmqtt.h => mqttclient/devicepluginmqttclient.h (91%) rename mqtt/devicepluginmqtt.json => mqttclient/devicepluginmqttclient.json (100%) create mode 100644 mqttclient/mqttclient.pro rename {mqtt => mqttclient}/translations/27c58205-07c8-4482-85ad-b435387803a5-en_US.ts (100%) diff --git a/debian/control b/debian/control index 0d299087..737b292b 100644 --- a/debian/control +++ b/debian/control @@ -285,6 +285,22 @@ Description: nymea.io plugin for mailnotification This package will install the nymea.io plugin for mailnotification +Package: nymea-plugin-mqttclient +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + nymea-plugins-translations, +Replaces: guh-plugin-mqtt +Description: nymea.io plugin for a generic MQTT client + The nymea daemon is a plugin based IoT (Internet of Things) server. The + server works like a translator for devices, things and services and + allows them to interact. + With the powerful rule engine you are able to connect any device available + in the system and create individual scenes and behaviors for your environment. + . + This package will install a generic MQTT client plugin for nymea.io + + Package: nymea-plugin-multisensor Architecture: any Depends: ${shlibs:Depends}, diff --git a/debian/nymea-plugin-mqttclient.install.in b/debian/nymea-plugin-mqttclient.install.in new file mode 100644 index 00000000..8ea2fae9 --- /dev/null +++ b/debian/nymea-plugin-mqttclient.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginmqttclient.so diff --git a/mqtt/mqtt.pro b/mqtt/mqtt.pro deleted file mode 100644 index 6a97ecc0..00000000 --- a/mqtt/mqtt.pro +++ /dev/null @@ -1,13 +0,0 @@ -include(../plugins.pri) - -QT += network - -TARGET = $$qtLibraryTarget(nymea_devicepluginmqtt) - -SOURCES += \ - devicepluginmqtt.cpp - -HEADERS += \ - devicepluginmqtt.h - - diff --git a/mqtt/devicepluginmqtt.cpp b/mqttclient/devicepluginmqttclient.cpp similarity index 92% rename from mqtt/devicepluginmqtt.cpp rename to mqttclient/devicepluginmqttclient.cpp index 69b7b638..5d99a6ea 100644 --- a/mqtt/devicepluginmqtt.cpp +++ b/mqttclient/devicepluginmqttclient.cpp @@ -48,22 +48,22 @@ For more details how to read this JSON file please check out the documentation for \l{The plugin JSON File}. - \quotefile plugins/deviceplugins/mqtt/devicepluginmqtt.json + \quotefile plugins/deviceplugins/mqtt/devicepluginmqttclient.json */ -#include "devicepluginmqtt.h" +#include "devicepluginmqttclient.h" #include "plugin/device.h" #include "plugininfo.h" #include "network/mqtt/mqttprovider.h" #include "nymea-mqtt/mqttclient.h" -DevicePluginMqtt::DevicePluginMqtt() +DevicePluginMqttClient::DevicePluginMqttClient() { } -DeviceManager::DeviceSetupStatus DevicePluginMqtt::setupDevice(Device *device) +DeviceManager::DeviceSetupStatus DevicePluginMqttClient::setupDevice(Device *device) { MqttClient *client = nullptr; if (device->deviceClassId() == internalMqttClientDeviceClassId) { @@ -83,8 +83,8 @@ DeviceManager::DeviceSetupStatus DevicePluginMqtt::setupDevice(Device *device) 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); + connect(client, &MqttClient::publishReceived, this, &DevicePluginMqttClient::publishReceived); + connect(client, &MqttClient::published, this, &DevicePluginMqttClient::published); // In case we're already connected, manually call subscribe now if (client->isConnected()) { subscribe(device); @@ -94,7 +94,7 @@ DeviceManager::DeviceSetupStatus DevicePluginMqtt::setupDevice(Device *device) } -DeviceManager::DeviceError DevicePluginMqtt::executeAction(Device *device, const Action &action) +DeviceManager::DeviceError DevicePluginMqttClient::executeAction(Device *device, const Action &action) { ParamTypeId topicParamTypeId = internalMqttClientTriggerActionTopicParamTypeId; ParamTypeId payloadParamTypeId = internalMqttClientTriggerActionDataParamTypeId; @@ -134,7 +134,7 @@ DeviceManager::DeviceError DevicePluginMqtt::executeAction(Device *device, const return DeviceManager::DeviceErrorAsync; } -void DevicePluginMqtt::subscribe(Device *device) +void DevicePluginMqttClient::subscribe(Device *device) { MqttClient *client = m_clients.value(device); if (!client) { @@ -148,7 +148,7 @@ void DevicePluginMqtt::subscribe(Device *device) } } -void DevicePluginMqtt::publishReceived(const QString &topic, const QByteArray &payload, bool retained) +void DevicePluginMqttClient::publishReceived(const QString &topic, const QByteArray &payload, bool retained) { qCDebug(dcMqttclient()) << "Publish received" << topic << payload << retained; @@ -171,7 +171,7 @@ void DevicePluginMqtt::publishReceived(const QString &topic, const QByteArray &p emitEvent(Event(eventTypeId, device->id(), ParamList() << Param(topicParamTypeId, topic) << Param(payloadParamTypeId, payload))); } -void DevicePluginMqtt::published(quint16 packetId) +void DevicePluginMqttClient::published(quint16 packetId) { if (!m_pendingPublishes.contains(packetId)) { return; @@ -180,7 +180,7 @@ void DevicePluginMqtt::published(quint16 packetId) emit actionExecutionFinished(m_pendingPublishes.take(packetId).id(), DeviceManager::DeviceErrorNoError); } -void DevicePluginMqtt::deviceRemoved(Device *device) +void DevicePluginMqttClient::deviceRemoved(Device *device) { qCDebug(dcMqttclient) << device; m_clients.take(device)->deleteLater(); diff --git a/mqtt/devicepluginmqtt.h b/mqttclient/devicepluginmqttclient.h similarity index 91% rename from mqtt/devicepluginmqtt.h rename to mqttclient/devicepluginmqttclient.h index 0289e4a1..7d3c2414 100644 --- a/mqtt/devicepluginmqtt.h +++ b/mqttclient/devicepluginmqttclient.h @@ -20,8 +20,8 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef DEVICEPLUGINMQTT_H -#define DEVICEPLUGINMQTT_H +#ifndef DEVICEPLUGINMQTTCLIENT_H +#define DEVICEPLUGINMQTTCLIENT_H #include "plugin/deviceplugin.h" @@ -31,14 +31,14 @@ class MqttClient; -class DevicePluginMqtt: public DevicePlugin +class DevicePluginMqttClient: public DevicePlugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginmqtt.json") + Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginmqttclient.json") Q_INTERFACES(DevicePlugin) public: - explicit DevicePluginMqtt(); + explicit DevicePluginMqttClient(); DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; void deviceRemoved(Device *device) override; @@ -58,4 +58,4 @@ private: QHash m_pendingPublishes; }; -#endif // DEVICEPLUGINMQTT_H +#endif // DEVICEPLUGINMQTTCLIENT_H diff --git a/mqtt/devicepluginmqtt.json b/mqttclient/devicepluginmqttclient.json similarity index 100% rename from mqtt/devicepluginmqtt.json rename to mqttclient/devicepluginmqttclient.json diff --git a/mqttclient/mqttclient.pro b/mqttclient/mqttclient.pro new file mode 100644 index 00000000..46b3c8ec --- /dev/null +++ b/mqttclient/mqttclient.pro @@ -0,0 +1,13 @@ +include(../plugins.pri) + +QT += network + +TARGET = $$qtLibraryTarget(nymea_devicepluginmqttclient) + +SOURCES += \ + devicepluginmqttclient.cpp + +HEADERS += \ + devicepluginmqttclient.h + + diff --git a/mqtt/translations/27c58205-07c8-4482-85ad-b435387803a5-en_US.ts b/mqttclient/translations/27c58205-07c8-4482-85ad-b435387803a5-en_US.ts similarity index 100% rename from mqtt/translations/27c58205-07c8-4482-85ad-b435387803a5-en_US.ts rename to mqttclient/translations/27c58205-07c8-4482-85ad-b435387803a5-en_US.ts diff --git a/nymea-plugins.pro b/nymea-plugins.pro index 5840818f..2782d88a 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -22,7 +22,7 @@ PLUGIN_DIRS = \ lgsmarttv \ mailnotification \ multisensor \ - mqtt \ + mqttclient \ netatmo \ networkdetector \ openweathermap \ From 49831a9074a02e9e88b614c322f683cb9adde24d Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 29 Nov 2018 13:19:57 +0100 Subject: [PATCH 14/16] bump version --- debian/changelog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/debian/changelog b/debian/changelog index fa7ebe2a..f3c04da1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,8 @@ +nymea-plugins (0.10.0) UNRELEASED; urgency=medium + + + -- Michael Zanetti Thu, 29 Nov 2018 12:48:11 +0100 + nymea-plugins (0.9.39) xenial; urgency=medium [ Jenkins ] From cc1fa9cd87801218b930ba003ff9b8d75a034ce8 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 29 Nov 2018 13:59:58 +0100 Subject: [PATCH 15/16] add mqtt plugin to maker plugin package --- debian/control | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/control b/debian/control index 737b292b..562615b3 100644 --- a/debian/control +++ b/debian/control @@ -742,6 +742,7 @@ Depends: nymea-plugin-commandlauncher, nymea-plugin-genericelements, nymea-plugin-avahimonitor, nymea-plugin-gpio, + nymea-plugin-mqtt, nymea-plugin-remotessh, Replaces: guh-plugins-maker Description: Plugins for nymea IoT server - Meta package for makers, tinkers and hackers From 81db70be2ac49d535a18da0289a1e936143ff4b1 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Fri, 30 Nov 2018 23:36:21 +0100 Subject: [PATCH 16/16] fix dpkg deps --- debian/control | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/debian/control b/debian/control index 562615b3..a0c0bbde 100644 --- a/debian/control +++ b/debian/control @@ -290,7 +290,6 @@ Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, nymea-plugins-translations, -Replaces: guh-plugin-mqtt Description: nymea.io plugin for a generic MQTT client The nymea daemon is a plugin based IoT (Internet of Things) server. The server works like a translator for devices, things and services and @@ -742,7 +741,7 @@ Depends: nymea-plugin-commandlauncher, nymea-plugin-genericelements, nymea-plugin-avahimonitor, nymea-plugin-gpio, - nymea-plugin-mqtt, + nymea-plugin-mqttclient, nymea-plugin-remotessh, Replaces: guh-plugins-maker Description: Plugins for nymea IoT server - Meta package for makers, tinkers and hackers