diff --git a/garadget/Garadget.pro b/garadget/Garadget.pro
new file mode 100644
index 00000000..b38efde7
--- /dev/null
+++ b/garadget/Garadget.pro
@@ -0,0 +1,14 @@
+include($$[QT_INSTALL_PREFIX]/include/nymea/plugin.pri)
+
+QT += network
+
+PKGCONFIG += nymea-mqtt
+
+TARGET = $$qtLibraryTarget(nymea_integrationpluginGaradget)
+
+SOURCES += \
+ integrationpluginGaradget.cpp
+
+HEADERS += \
+ integrationpluginGaradget.h
+
diff --git a/garadget/Garadget.pro.user b/garadget/Garadget.pro.user
new file mode 100644
index 00000000..fb6e084f
--- /dev/null
+++ b/garadget/Garadget.pro.user
@@ -0,0 +1,323 @@
+
+
+
+
+
+ EnvironmentId
+ {ba25b4ed-600c-4ef0-9f6e-aee74c4d5771}
+
+
+ ProjectExplorer.Project.ActiveTarget
+ 0
+
+
+ ProjectExplorer.Project.EditorSettings
+
+ true
+ false
+ true
+
+ Cpp
+
+ CppGlobal
+
+
+
+ QmlJS
+
+ QmlJSGlobal
+
+
+ 2
+ UTF-8
+ false
+ 4
+ false
+ 80
+ true
+ true
+ 1
+ true
+ false
+ 0
+ true
+ true
+ 0
+ 8
+ true
+ 1
+ true
+ true
+ true
+ *.md, *.MD, Makefile
+ false
+ true
+
+
+
+ ProjectExplorer.Project.PluginSettings
+
+
+ true
+ true
+ true
+ true
+ true
+
+
+ 0
+ true
+
+ true
+ Builtin.Questionable
+
+ true
+ true
+ Builtin.DefaultTidyAndClazy
+ 4
+
+
+
+ true
+
+
+
+
+
+ ProjectExplorer.Project.Target.0
+
+ Desktop
+ Desktop
+ Desktop
+ {42208f61-3dc1-4538-a721-4593dcfd5243}
+ 0
+ 0
+ 0
+
+ 0
+ /home/beckstein/nymea-testing/build-Garadget-Desktop-Debug
+ /home/beckstein/nymea-testing/build-Garadget-Desktop-Debug
+
+
+ true
+ QtProjectManager.QMakeBuildStep
+
+ false
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+
+ 2
+ Build
+ Build
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+ clean
+
+ 1
+ Clean
+ Clean
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+
+ Debug
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 2
+ 0
+
+
+ /home/beckstein/nymea-testing/build-Garadget-Desktop-Release
+ /home/beckstein/nymea-testing/build-Garadget-Desktop-Release
+
+
+ true
+ QtProjectManager.QMakeBuildStep
+
+ false
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+
+ 2
+ Build
+ Build
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+ clean
+
+ 1
+ Clean
+ Clean
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+
+ Release
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ 0
+
+
+ 0
+ /home/beckstein/nymea-testing/build-Garadget-Desktop-Profile
+ /home/beckstein/nymea-testing/build-Garadget-Desktop-Profile
+
+
+ true
+ QtProjectManager.QMakeBuildStep
+
+ false
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+
+ 2
+ Build
+ Build
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Qt4ProjectManager.MakeStep
+ clean
+
+ 1
+ Clean
+ Clean
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+
+ Profile
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ 0
+ 0
+
+ 3
+
+
+ 0
+ Deploy
+ Deploy
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+
+ false
+ ProjectExplorer.DefaultDeployConfiguration
+
+ 1
+
+ dwarf
+
+ cpu-cycles
+
+
+ 250
+
+ -e
+ cpu-cycles
+ --call-graph
+ dwarf,4096
+ -F
+ 250
+
+ -F
+ true
+ 4096
+ false
+ false
+ 1000
+
+ true
+
+ false
+ false
+ false
+ false
+ true
+ 0.01
+ 10
+ true
+ kcachegrind
+ 1
+ 25
+
+ 1
+ true
+ false
+ true
+ valgrind
+
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+
+
+ 2
+
+ LD_LIBRARY_PATH=/home/beckstein/nymea-testing/build-nymea-Desktop-Debug/libnymea/:home/beckstein/nymea-testing/build-nymea-Desktop-Debug/libnymea-core/
+ NYMEA_PLUGINS_PATH=/home/beckstein/nymea-testing/build-Garadget-Desktop-Debug
+
+
+ /usr/bin/nymead
+ ProjectExplorer.CustomExecutableRunConfiguration
+
+ -n -d Garadget
+ false
+ true
+ false
+ true
+ /home/beckstein/nymea-testing/working
+
+ 1
+
+
+
+ ProjectExplorer.Project.TargetCount
+ 1
+
+
+ ProjectExplorer.Project.Updater.FileVersion
+ 22
+
+
+ Version
+ 22
+
+
diff --git a/garadget/LICENSE.LGPL3 b/garadget/LICENSE.LGPL3
new file mode 100644
index 00000000..0a041280
--- /dev/null
+++ b/garadget/LICENSE.LGPL3
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/garadget/README.md b/garadget/README.md
new file mode 100644
index 00000000..79fb1ac1
--- /dev/null
+++ b/garadget/README.md
@@ -0,0 +1,4 @@
+# Garadget
+--------------------------------
+
+Description of the plugin...
diff --git a/garadget/debian/changelog b/garadget/debian/changelog
new file mode 100644
index 00000000..2aba7598
--- /dev/null
+++ b/garadget/debian/changelog
@@ -0,0 +1,5 @@
+nymea-plugin-Garadget (0.0.1) xenial; urgency=medium
+
+ * Initial release.
+
+ -- Bruce Eckstein Mon, 21 Aug 2017 10:12:00 +0000
diff --git a/garadget/debian/compat b/garadget/debian/compat
new file mode 100644
index 00000000..ec635144
--- /dev/null
+++ b/garadget/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/garadget/debian/control b/garadget/debian/control
new file mode 100644
index 00000000..b1e138bb
--- /dev/null
+++ b/garadget/debian/control
@@ -0,0 +1,25 @@
+Source: nymea-plugin-Garadget
+Section: utils
+Priority: options
+Maintainer: Bruce Eckstein
+Build-depends: debhelper (>= 9.0.0),
+ libnymea1-dev,
+ nymea-dev-tools:native,
+ qt5-qmake:native,
+ pkg-config,
+Standards-Version: 3.9.3
+
+Package: nymea-plugin-Garadget
+Architecture: any
+Multi-Arch: same
+Section: libs
+Depends: ${shlibs:Depends},
+ ${misc:Depends},
+Description: nymea.io plugin for Garadget
+ 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 Garadget
diff --git a/garadget/debian/copyright b/garadget/debian/copyright
new file mode 100644
index 00000000..ab5fccbe
--- /dev/null
+++ b/garadget/debian/copyright
@@ -0,0 +1,14 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: ${ProjectName}
+Upstream-Contact: Bruce Eckstein
+Copyright: 2020, Bruce Eckstein
+
+
+License: LGPL-3
+ On Debian systems, the complete text of the GNU Lesser General
+ Public License can be found in `/usr/share/common-licenses/LGPL-3'.
+
+Files: *
+License: LGPL-3
+Copyright: Bruce Eckstein
+
diff --git a/garadget/debian/nymea-plugin-Garadget.install.in b/garadget/debian/nymea-plugin-Garadget.install.in
new file mode 100644
index 00000000..889763ba
--- /dev/null
+++ b/garadget/debian/nymea-plugin-Garadget.install.in
@@ -0,0 +1 @@
+usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginGaradget.so
diff --git a/garadget/debian/rules b/garadget/debian/rules
new file mode 100644
index 00000000..2c4801ea
--- /dev/null
+++ b/garadget/debian/rules
@@ -0,0 +1,25 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+
+export DH_VERBOSE=1
+DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
+
+PREPROCESS_FILES := $(wildcard debian/*.in)
+
+$(PREPROCESS_FILES:.in=): %: %.in
+ sed 's,/@DEB_HOST_MULTIARCH@,$(DEB_HOST_MULTIARCH:%=/%),g' $< > $@
+
+%:
+ dh $@ --parallel
+
+override_dh_install: $(PREPROCESS_FILES:.in=)
+ make -j9 install DESTDIR=debian/tmp AM_UPDATE_INFO_DIR=no INSTALL_ROOT=debian/tmp
+ dh_install --fail-missing
+
+override_dh_auto_build:
+ dh_auto_build
+ make lrelease
+
+override_dh_auto_clean:
+ dh_auto_clean
+ rm -rf $(PREPROCESS_FILES:.in=)
diff --git a/garadget/debian/source/format b/garadget/debian/source/format
new file mode 100644
index 00000000..89ae9db8
--- /dev/null
+++ b/garadget/debian/source/format
@@ -0,0 +1 @@
+3.0 (native)
diff --git a/garadget/garadget.svg b/garadget/garadget.svg
new file mode 100644
index 00000000..a5a7e695
--- /dev/null
+++ b/garadget/garadget.svg
@@ -0,0 +1,128 @@
+
+
diff --git a/garadget/integrationpluginGaradget.cpp b/garadget/integrationpluginGaradget.cpp
new file mode 100644
index 00000000..db7ee9d0
--- /dev/null
+++ b/garadget/integrationpluginGaradget.cpp
@@ -0,0 +1,278 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project 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 project. If not, see .
+*
+* For any further details and any questions please contact us under
+* contact@nymea.io or see our FAQ/Licensing Information on
+* https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "integrationpluginGaradget.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"
+
+static QHash sonoff_basicPowerStateTypeIds = {
+ {"POWER1", sonoff_basicPowerStateTypeId},
+};
+static QHash> stateMaps = {
+ {sonoff_basicThingClassId, sonoff_basicPowerStateTypeIds},
+};
+
+IntegrationPluginGaradget::IntegrationPluginGaradget()
+{
+ // Helper maps for parent devices (aka sonoff_*)
+ m_ipAddressParamTypeMap[sonoff_basicThingClassId] = sonoff_basicThingIpAddressParamTypeId;
+
+
+ // Helper maps for all devices
+ m_connectedStateTypeMap[sonoff_basicThingClassId] = sonoff_basicConnectedStateTypeId;
+
+ m_signalStrengthStateTypeMap[sonoff_basicThingClassId] = sonoff_basicSignalStrengthStateTypeId;
+}
+
+IntegrationPluginGaradget::~IntegrationPluginGaradget()
+{
+}
+
+void IntegrationPluginGaradget::init()
+{
+}
+
+void IntegrationPluginGaradget::setupThing(ThingSetupInfo *info)
+{
+ Thing *thing = info->thing();
+
+ if (m_ipAddressParamTypeMap.contains(thing->thingClassId())) {
+ ParamTypeId ipAddressParamTypeId = m_ipAddressParamTypeMap.value(thing->thingClassId());
+
+ QHostAddress deviceAddress = QHostAddress(thing->paramValue(ipAddressParamTypeId).toString());
+ if (deviceAddress.isNull()) {
+ qCWarning(dcGaradget) << "Not a valid IP address given for IP address parameter";
+ //: Error setting up thing
+ return info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The given IP address is not valid."));
+ }
+ MqttChannel *channel = hardwareManager()->mqttProvider()->createChannel(thing->id().toString().remove(QRegExp("[{}-]")), deviceAddress );
+ if (!channel) {
+ qCWarning(dcGaradget) << "Failed to create MQTT channel.";
+ //: Error setting up thing
+ return info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Error creating MQTT channel. Please check MQTT server settings."));
+ }
+// ipaddress is defined here but not sure how to get rid of and still have the mqtt channel. Garadget only works with the broker.
+
+ qCDebug(dcGaradget) << "BEE initial setup instructions:" << deviceAddress.toString() << channel;
+
+ m_mqttChannels.insert(info->thing(), channel);
+ connect(channel, &MqttChannel::clientConnected, this, &IntegrationPluginGaradget::onClientConnected);
+ connect(channel, &MqttChannel::clientDisconnected, this, &IntegrationPluginGaradget::onClientDisconnected);
+ connect(channel, &MqttChannel::publishReceived, this, &IntegrationPluginGaradget::onPublishReceived);
+
+ qCDebug(dcGaradget) << "Garadget setup complete";
+ info->finish(Thing::ThingErrorNoError);
+ foreach (Thing *child, myThings()) {
+ if (child->parentId() == info->thing()->id()) {
+ // Already have child devices... We're done here
+ return;
+ }
+ }
+
+// insert to reprogram client Garadget to new device/topic name
+ QJsonObject beeobj;
+ beeobj.insert("nme", QString(channel->topicPrefixList().first()));
+ QJsonDocument beedoc(beeobj);
+ QByteArray beedata = beedoc.toJson(QJsonDocument::Compact);
+ QString jsonString(beedata);
+ qCDebug(dcGaradget) << "Publishing:" << "garadget/garage/set-conf " << beedata;
+// next line will not publish to the correct device as topic is 32 characters long and garadget only allows 30 characters.
+ channel->publish("garadget/garaged/set-config", beedata);
+ channel->publish("garadget/garage/command", "get-config");
+// need to develop test for correct modification of nme
+ return;
+ }
+
+ if (m_connectedStateTypeMap.contains(thing->thingClassId())) {
+ Thing* parentDevice = myThings().findById(thing->parentId());
+ StateTypeId connectedStateTypeId = m_connectedStateTypeMap.value(thing->thingClassId());
+ thing->setStateValue(m_connectedStateTypeMap.value(thing->thingClassId()), parentDevice->stateValue(connectedStateTypeId));
+ return info->finish(Thing::ThingErrorNoError);
+ }
+
+ qCWarning(dcGaradget) << "Unhandled ThingClass in setupDevice" << thing->thingClassId();
+}
+
+void IntegrationPluginGaradget::thingRemoved(Thing *thing)
+{
+ qCDebug(dcGaradget) << "Device removed" << thing->name();
+ if (m_mqttChannels.contains(thing)) {
+ qCDebug(dcGaradget) << "Releasing MQTT channel";
+ MqttChannel* channel = m_mqttChannels.take(thing);
+ hardwareManager()->mqttProvider()->releaseChannel(channel);
+ }
+}
+
+void IntegrationPluginGaradget::executeAction(ThingActionInfo *info)
+{
+ Thing *thing = info->thing();
+ Action action = info->action();
+
+ if (thing->thingClassId() == sonoff_basicThingClassId) {
+ MqttChannel *channel = m_mqttChannels.value(thing);
+ if (!channel) {
+ qCWarning(dcGaradget()) << "No MQTT channel for this thing.";
+ info->finish(Thing::ThingErrorHardwareNotAvailable);
+ return;
+ }
+ QString channelName = stateMaps.value(thing->thingClassId()).key(action.actionTypeId());
+ QByteArray payload = action.paramValue(action.actionTypeId()).toBool() ? "ON" : "OFF";
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + channelName << payload;
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + channelName, payload);
+ thing->setStateValue(action.actionTypeId(), action.paramValue(action.actionTypeId()));
+ info->finish(Thing::ThingErrorNoError);
+ return;
+ }
+
+ // Legacy (deprecated) connected devices
+ if (m_powerStateTypeMap.contains(thing->thingClassId())) {
+ Thing *parentDev = myThings().findById(thing->parentId());
+ MqttChannel *channel = m_mqttChannels.value(parentDev);
+ if (!channel) {
+ qCWarning(dcGaradget()) << "No mqtt channel for this thing.";
+ return info->finish(Thing::ThingErrorHardwareNotAvailable);
+ }
+ ParamTypeId channelParamTypeId = m_channelParamTypeMap.value(thing->thingClassId());
+ ParamTypeId powerActionParamTypeId = ParamTypeId(m_powerStateTypeMap.value(thing->thingClassId()).toString());
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(channelParamTypeId).toString() << (action.param(powerActionParamTypeId).value().toBool() ? "ON" : "OFF");
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(channelParamTypeId).toString().toLower(), action.param(powerActionParamTypeId).value().toBool() ? "ON" : "OFF");
+ thing->setStateValue(m_powerStateTypeMap.value(thing->thingClassId()), action.param(powerActionParamTypeId).value().toBool());
+ return info->finish(Thing::ThingErrorNoError);
+ }
+ if (m_closableStopActionTypeMap.contains(thing->thingClassId())) {
+ Thing *parentDev = myThings().findById(thing->parentId());
+ MqttChannel *channel = m_mqttChannels.value(parentDev);
+ if (!channel) {
+ qCWarning(dcGaradget()) << "No mqtt channel for this thing.";
+ return info->finish(Thing::ThingErrorHardwareNotAvailable);
+ }
+ ParamTypeId openingChannelParamTypeId = m_openingChannelParamTypeMap.value(thing->thingClassId());
+ ParamTypeId closingChannelParamTypeId = m_closingChannelParamTypeMap.value(thing->thingClassId());
+ if (action.actionTypeId() == m_closableOpenActionTypeMap.value(thing->thingClassId())) {
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString() << "OFF";
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString().toLower(), "OFF");
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString() << "ON";
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString().toLower(), "ON");
+ } else if (action.actionTypeId() == m_closableCloseActionTypeMap.value(thing->thingClassId())) {
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString() << "OFF";
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString().toLower(), "OFF");
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString() << "ON";
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString().toLower(), "ON");
+ } else { // Stop
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString() << "OFF";
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString().toLower(), "OFF");
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString() << "OFF";
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString().toLower(), "OFF");
+ }
+ return info->finish(Thing::ThingErrorNoError);
+ }
+ qCWarning(dcGaradget) << "Unhandled execute action call for devie" << thing;
+}
+
+void IntegrationPluginGaradget::onClientConnected(MqttChannel *channel)
+{
+ qCDebug(dcGaradget) << "Sonoff thing connected!";
+ Thing *dev = m_mqttChannels.key(channel);
+ dev->setStateValue(m_connectedStateTypeMap.value(dev->thingClassId()), true);
+
+ foreach (Thing *child, myThings()) {
+ if (child->parentId() == dev->id()) {
+ child->setStateValue(m_connectedStateTypeMap.value(child->thingClassId()), true);
+ }
+ }
+}
+
+void IntegrationPluginGaradget::onClientDisconnected(MqttChannel *channel)
+{
+ qCDebug(dcGaradget) << "Sonoff thing disconnected!";
+ Thing *dev = m_mqttChannels.key(channel);
+ dev->setStateValue(m_connectedStateTypeMap.value(dev->thingClassId()), false);
+
+ foreach (Thing *child, myThings()) {
+ if (child->parentId() == dev->id()) {
+ child->setStateValue(m_connectedStateTypeMap.value(child->thingClassId()), false);
+ }
+ }
+}
+
+void IntegrationPluginGaradget::onPublishReceived(MqttChannel *channel, const QString &topic, const QByteArray &payload)
+{
+ qCDebug(dcGaradget) << "Publish received from Sonoff thing:" << topic << qUtf8Printable(payload);
+ Thing *thing = m_mqttChannels.key(channel);
+ if (topic.startsWith(channel->topicPrefixList().first() + "/sonoff/POWER")) {
+ QString channelName = topic.split("/").last();
+
+ thing->setStateValue(stateMaps.value(thing->thingClassId()).value(channelName), payload == "ON");
+
+ // Legacy (deprecated) connected things via params
+ foreach (Thing *child, myThings().filterByParentId(thing->id())) {
+ if (child->paramValue(m_channelParamTypeMap.value(child->thingClassId())).toString() != channelName) {
+ continue;
+ }
+ if (m_powerStateTypeMap.contains(child->thingClassId())) {
+ child->setStateValue(m_powerStateTypeMap.value(child->thingClassId()), payload == "ON");
+ }
+ }
+ }
+ if (topic.startsWith(channel->topicPrefixList().first() + "/sonoff/STATE")) {
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &error);
+ if (error.error != QJsonParseError::NoError) {
+ qCWarning(dcGaradget) << "Cannot parse JSON from Garadget device" << error.errorString();
+ return;
+ }
+ QVariantMap dataMap = jsonDoc.toVariant().toMap();
+ thing->setStateValue(m_signalStrengthStateTypeMap.value(thing->thingClassId()), dataMap.value("Wifi").toMap().value("RSSI").toInt());
+
+ if (m_brightnessStateTypeMap.contains(thing->thingClassId())) {
+ thing->setStateValue(m_brightnessStateTypeMap.value(thing->thingClassId()), dataMap.value("Dimmer").toInt());
+ }
+
+ // Legacy (deprecated) connected things by params
+ foreach (Thing *child, myThings().filterByParentId(thing->id())) {
+ if (m_powerStateTypeMap.contains(child->thingClassId())) {
+ QString childChannel = child->paramValue(m_channelParamTypeMap.value(child->thingClassId())).toString();
+ QString valueString = jsonDoc.toVariant().toMap().value(childChannel).toString();
+ child->setStateValue(m_powerStateTypeMap.value(child->thingClassId()), valueString == "ON");
+ }
+ child->setStateValue(m_signalStrengthStateTypeMap.value(child->thingClassId()), dataMap.value("Wifi").toMap().value("RSSI").toInt());
+ }
+ }
+}
+
diff --git a/garadget/integrationpluginGaradget.cpp~ b/garadget/integrationpluginGaradget.cpp~
new file mode 100644
index 00000000..3f584483
--- /dev/null
+++ b/garadget/integrationpluginGaradget.cpp~
@@ -0,0 +1,301 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project 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 project. If not, see .
+*
+* For any further details and any questions please contact us under
+* contact@nymea.io or see our FAQ/Licensing Information on
+* https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "integrationpluginGaradget.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"
+
+static QHash sonoff_basicPowerStateTypeIds = {
+ {"POWER1", sonoff_basicPowerStateTypeId},
+};
+static QHash> stateMaps = {
+ {sonoff_basicThingClassId, sonoff_basicPowerStateTypeIds},
+};
+
+IntegrationPluginGaradget::IntegrationPluginGaradget()
+{
+ // Helper maps for parent devices (aka sonoff_*)
+ m_ipAddressParamTypeMap[sonoff_basicThingClassId] = sonoff_basicThingIpAddressParamTypeId;
+
+
+ // Helper maps for all devices
+ m_connectedStateTypeMap[sonoff_basicThingClassId] = sonoff_basicConnectedStateTypeId;
+
+ m_signalStrengthStateTypeMap[sonoff_basicThingClassId] = sonoff_basicSignalStrengthStateTypeId;
+}
+
+IntegrationPluginGaradget::~IntegrationPluginGaradget()
+{
+}
+
+void IntegrationPluginGaradget::init()
+{
+}
+
+void IntegrationPluginGaradget::setupThing(ThingSetupInfo *info)
+{
+ Thing *thing = info->thing();
+
+ if (m_ipAddressParamTypeMap.contains(thing->thingClassId())) {
+ ParamTypeId ipAddressParamTypeId = m_ipAddressParamTypeMap.value(thing->thingClassId());
+
+ QHostAddress deviceAddress = QHostAddress(thing->paramValue(ipAddressParamTypeId).toString());
+ if (deviceAddress.isNull()) {
+ qCWarning(dcGaradget) << "Not a valid IP address given for IP address parameter";
+ //: Error setting up thing
+ return info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The given IP address is not valid."));
+ }
+
+ MqttChannel *channel = hardwareManager()->mqttProvider()->createChannel(thing->id().toString().remove(QRegExp("[{}-]")), deviceAddress );
+ if (!channel) {
+ qCWarning(dcGaradget) << "Failed to create MQTT channel.";
+ //: Error setting up thing
+ return info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Error creating MQTT channel. Please check MQTT server settings."));
+ }
+// ipaddress is defined here but not sure how to get rid of and still have the mqtt channel. Garadget only works with the broker.
+
+ qCDebug(dcGaradget) << "BEE initial setup instructions:" << deviceAddress.toString() << channel;
+
+ m_mqttChannels.insert(info->thing(), channel);
+ connect(channel, &MqttChannel::clientConnected, this, &IntegrationPluginGaradget::onClientConnected);
+ connect(channel, &MqttChannel::clientDisconnected, this, &IntegrationPluginGaradget::onClientDisconnected);
+ connect(channel, &MqttChannel::publishReceived, this, &IntegrationPluginGaradget::onPublishReceived);
+
+ qCDebug(dcGaradget) << "Garadget setup complete";
+ info->finish(Thing::ThingErrorNoError);
+ foreach (Thing *child, myThings()) {
+ if (child->parentId() == info->thing()->id()) {
+ // Already have child devices... We're done here
+ return;
+ }
+ }
+
+// insert to reprogram client Garadget to new device/topic name
+ QJsonObject beeobj;
+ beeobj.insert("nme", QString(channel->topicPrefixList().first()));
+ QJsonDocument beedoc(beeobj);
+ QByteArray beedata = beedoc.toJson(QJsonDocument::Compact);
+ QString jsonString(beedata);
+ qCDebug(dcGaradget) << "Publishing:" << "garadget/garage/set-conf " << beedata;
+// next line will not publish to the correct device as topic is 32 characters long and garadget only allows 30 characters.
+ channel->publish("garadget/garaged/set-config", beedata);
+ channel->publish("garadget/garage/command", "get-config");
+// need to develop test for correct modification of nme
+ return;
+ }
+
+ if (m_connectedStateTypeMap.contains(thing->thingClassId())) {
+ Thing* parentDevice = myThings().findById(thing->parentId());
+ StateTypeId connectedStateTypeId = m_connectedStateTypeMap.value(thing->thingClassId());
+ thing->setStateValue(m_connectedStateTypeMap.value(thing->thingClassId()), parentDevice->stateValue(connectedStateTypeId));
+ return info->finish(Thing::ThingErrorNoError);
+ }
+
+ qCWarning(dcGaradget) << "Unhandled ThingClass in setupDevice" << thing->thingClassId();
+}
+
+void IntegrationPluginGaradget::onPublishReceivedSetup(MqttChannel *channel, const QString &topic, const QByteArray &payload)
+{
+ qCDebug(dcGaradget) << "Publish received from garadget/garage thing:" << topic << qUtf8Printable(payload);
+ Thing *thing = m_mqttChannels.key(channel);
+ if (topic.startsWith( "garadget/garage/config")) {
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &error);
+ qCWarning(dcGaradget) << "BEE on receive QJsonDocument: " << jsonDoc;
+ if (error.error != QJsonParseError::NoError) {
+ qCWarning(dcGaradget) << "Cannot parse JSON from Garadget device" << error.errorString();
+ return;
+ }
+ QVariantMap dataMap = jsonDoc.toVariant().toMap();
+ thing->setStateValue(m_signalStrengthStateTypeMap.value(thing->thingClassId()), dataMap.value("Wifi").toMap().value("RSSI").toInt());
+
+ }
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &error);
+ qCWarning(dcGaradget) << "BEE on receive ending QJsonDocument: " << jsonDoc;
+}
+
+
+void IntegrationPluginGaradget::thingRemoved(Thing *thing)
+{
+ qCDebug(dcGaradget) << "Device removed" << thing->name();
+ if (m_mqttChannels.contains(thing)) {
+ qCDebug(dcGaradget) << "Releasing MQTT channel";
+ MqttChannel* channel = m_mqttChannels.take(thing);
+ hardwareManager()->mqttProvider()->releaseChannel(channel);
+ }
+}
+
+void IntegrationPluginGaradget::executeAction(ThingActionInfo *info)
+{
+ Thing *thing = info->thing();
+ Action action = info->action();
+
+ if (thing->thingClassId() == sonoff_basicThingClassId) {
+ MqttChannel *channel = m_mqttChannels.value(thing);
+ if (!channel) {
+ qCWarning(dcGaradget()) << "No MQTT channel for this thing.";
+ info->finish(Thing::ThingErrorHardwareNotAvailable);
+ return;
+ }
+ QString channelName = stateMaps.value(thing->thingClassId()).key(action.actionTypeId());
+ QByteArray payload = action.paramValue(action.actionTypeId()).toBool() ? "ON" : "OFF";
+ qCDebug(dcGaradget) << "Publishing:" << "garadget/" + channel->topicPrefixList().first() + "/command " + channelName << payload;
+ channel->publish("garadget/" + channel->topicPrefixList().first() + "/command " + channelName, payload);
+ thing->setStateValue(action.actionTypeId(), action.paramValue(action.actionTypeId()));
+ info->finish(Thing::ThingErrorNoError);
+ return;
+ }
+
+ // Legacy (deprecated) connected devices
+ if (m_powerStateTypeMap.contains(thing->thingClassId())) {
+ Thing *parentDev = myThings().findById(thing->parentId());
+ MqttChannel *channel = m_mqttChannels.value(parentDev);
+ if (!channel) {
+ qCWarning(dcGaradget()) << "No mqtt channel for this thing.";
+ return info->finish(Thing::ThingErrorHardwareNotAvailable);
+ }
+ ParamTypeId channelParamTypeId = m_channelParamTypeMap.value(thing->thingClassId());
+ ParamTypeId powerActionParamTypeId = ParamTypeId(m_powerStateTypeMap.value(thing->thingClassId()).toString());
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(channelParamTypeId).toString() << (action.param(powerActionParamTypeId).value().toBool() ? "ON" : "OFF");
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(channelParamTypeId).toString().toLower(), action.param(powerActionParamTypeId).value().toBool() ? "ON" : "OFF");
+ thing->setStateValue(m_powerStateTypeMap.value(thing->thingClassId()), action.param(powerActionParamTypeId).value().toBool());
+ return info->finish(Thing::ThingErrorNoError);
+ }
+ if (m_closableStopActionTypeMap.contains(thing->thingClassId())) {
+ Thing *parentDev = myThings().findById(thing->parentId());
+ MqttChannel *channel = m_mqttChannels.value(parentDev);
+ if (!channel) {
+ qCWarning(dcGaradget()) << "No mqtt channel for this thing.";
+ return info->finish(Thing::ThingErrorHardwareNotAvailable);
+ }
+ ParamTypeId openingChannelParamTypeId = m_openingChannelParamTypeMap.value(thing->thingClassId());
+ ParamTypeId closingChannelParamTypeId = m_closingChannelParamTypeMap.value(thing->thingClassId());
+ if (action.actionTypeId() == m_closableOpenActionTypeMap.value(thing->thingClassId())) {
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString() << "OFF";
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString().toLower(), "OFF");
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString() << "ON";
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString().toLower(), "ON");
+ } else if (action.actionTypeId() == m_closableCloseActionTypeMap.value(thing->thingClassId())) {
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString() << "OFF";
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString().toLower(), "OFF");
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString() << "ON";
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString().toLower(), "ON");
+ } else { // Stop
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString() << "OFF";
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString().toLower(), "OFF");
+ qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString() << "OFF";
+ channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString().toLower(), "OFF");
+ }
+ return info->finish(Thing::ThingErrorNoError);
+ }
+ qCWarning(dcGaradget) << "Unhandled execute action call for devie" << thing;
+}
+
+void IntegrationPluginGaradget::onClientConnected(MqttChannel *channel)
+{
+ qCDebug(dcGaradget) << "Sonoff thing connected!";
+ Thing *dev = m_mqttChannels.key(channel);
+ dev->setStateValue(m_connectedStateTypeMap.value(dev->thingClassId()), true);
+
+ foreach (Thing *child, myThings()) {
+ if (child->parentId() == dev->id()) {
+ child->setStateValue(m_connectedStateTypeMap.value(child->thingClassId()), true);
+ }
+ }
+}
+
+void IntegrationPluginGaradget::onClientDisconnected(MqttChannel *channel)
+{
+ qCDebug(dcGaradget) << "Sonoff thing disconnected!";
+ Thing *dev = m_mqttChannels.key(channel);
+ dev->setStateValue(m_connectedStateTypeMap.value(dev->thingClassId()), false);
+
+ foreach (Thing *child, myThings()) {
+ if (child->parentId() == dev->id()) {
+ child->setStateValue(m_connectedStateTypeMap.value(child->thingClassId()), false);
+ }
+ }
+}
+
+void IntegrationPluginGaradget::onPublishReceived(MqttChannel *channel, const QString &topic, const QByteArray &payload)
+{
+ qCDebug(dcGaradget) << "Publish received from Sonoff thing:" << topic << qUtf8Printable(payload);
+ Thing *thing = m_mqttChannels.key(channel);
+ if (topic.startsWith(channel->topicPrefixList().first() + "/sonoff/POWER")) {
+ QString channelName = topic.split("/").last();
+
+ thing->setStateValue(stateMaps.value(thing->thingClassId()).value(channelName), payload == "ON");
+
+ // Legacy (deprecated) connected things via params
+ foreach (Thing *child, myThings().filterByParentId(thing->id())) {
+ if (child->paramValue(m_channelParamTypeMap.value(child->thingClassId())).toString() != channelName) {
+ continue;
+ }
+ if (m_powerStateTypeMap.contains(child->thingClassId())) {
+ child->setStateValue(m_powerStateTypeMap.value(child->thingClassId()), payload == "ON");
+ }
+ }
+ }
+ if (topic.startsWith(channel->topicPrefixList().first() + "/sonoff/STATE")) {
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &error);
+ if (error.error != QJsonParseError::NoError) {
+ qCWarning(dcGaradget) << "Cannot parse JSON from Garadget device" << error.errorString();
+ return;
+ }
+ QVariantMap dataMap = jsonDoc.toVariant().toMap();
+ thing->setStateValue(m_signalStrengthStateTypeMap.value(thing->thingClassId()), dataMap.value("Wifi").toMap().value("RSSI").toInt());
+
+ if (m_brightnessStateTypeMap.contains(thing->thingClassId())) {
+ thing->setStateValue(m_brightnessStateTypeMap.value(thing->thingClassId()), dataMap.value("Dimmer").toInt());
+ }
+
+ // Legacy (deprecated) connected things by params
+ foreach (Thing *child, myThings().filterByParentId(thing->id())) {
+ if (m_powerStateTypeMap.contains(child->thingClassId())) {
+ QString childChannel = child->paramValue(m_channelParamTypeMap.value(child->thingClassId())).toString();
+ QString valueString = jsonDoc.toVariant().toMap().value(childChannel).toString();
+ child->setStateValue(m_powerStateTypeMap.value(child->thingClassId()), valueString == "ON");
+ }
+ child->setStateValue(m_signalStrengthStateTypeMap.value(child->thingClassId()), dataMap.value("Wifi").toMap().value("RSSI").toInt());
+ }
+ }
+}
+
diff --git a/garadget/integrationpluginGaradget.h b/garadget/integrationpluginGaradget.h
new file mode 100644
index 00000000..5d1c4213
--- /dev/null
+++ b/garadget/integrationpluginGaradget.h
@@ -0,0 +1,84 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project 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 project. If not, see .
+*
+* For any further details and any questions please contact us under
+* contact@nymea.io or see our FAQ/Licensing Information on
+* https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef INTEGRATIONPLUGINGARADGET_H
+#define INTEGRATIONPLUGINGARADGET_H
+
+#include "integrations/integrationplugin.h"
+
+class MqttChannel;
+
+class IntegrationPluginGaradget: public IntegrationPlugin
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginGaradget.json")
+ Q_INTERFACES(IntegrationPlugin)
+
+
+public:
+ explicit IntegrationPluginGaradget();
+ ~IntegrationPluginGaradget();
+
+ void init() override;
+ void setupThing(ThingSetupInfo *info) override;
+ void thingRemoved(Thing *thing) override;
+ void executeAction(ThingActionInfo *info) 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 Garadget)
+ QHash m_channelParamTypeMap;
+ QHash m_openingChannelParamTypeMap;
+ QHash m_closingChannelParamTypeMap;
+ QHash m_powerStateTypeMap;
+
+ QHash m_closableOpenActionTypeMap;
+ QHash m_closableCloseActionTypeMap;
+ QHash m_closableStopActionTypeMap;
+
+ // Helpers for both devices
+ QHash m_connectedStateTypeMap;
+ QHash m_signalStrengthStateTypeMap;
+
+ QHash m_brightnessStateTypeMap;
+};
+
+#endif // INTEGRATIONPLUGINGARADGET_H
diff --git a/garadget/integrationpluginGaradget.json b/garadget/integrationpluginGaradget.json
new file mode 100644
index 00000000..cbf896d4
--- /dev/null
+++ b/garadget/integrationpluginGaradget.json
@@ -0,0 +1,78 @@
+{
+ "name": "Garadget",
+ "displayName": "Sonoff-Garadget",
+ "id": "476781af-ad41-423b-b9bb-f308f2aff339",
+ "vendors": [
+ {
+ "name": "Garadget",
+ "displayName": "Garadget",
+ "id": "22d59bee-79cc-4dea-b742-30ac6bdcf2c9",
+ "thingClasses": [
+ {
+ "name": "sonoff_basic",
+ "displayName": "Garadget",
+ "id": "e808b8ae-7608-41ce-8444-892f0648a4d3",
+ "setupMethod": "JustAdd",
+ "createMethods": ["User"],
+ "interfaces": [ ],
+ "paramTypes": [
+ {
+ "id": "54a11a17-fc37-4316-891f-001c55e38220",
+ "name":"ipAddress",
+ "displayName": "IP address",
+ "type": "QString"
+ }
+
+ ],
+ "stateTypes": [
+ {
+ "id": "a3d7e6eb-82d0-47ee-b95f-4dde931eb7e2",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "defaultValue": false,
+ "cached": false
+ },
+ {
+ "id": "e276e6ca-4cb5-46d2-9a78-31f92d78ac70",
+ "name": "signalStrength",
+ "displayName": "Signal strength",
+ "displayNameEvent": "Signal strength changed",
+ "type": "uint",
+ "unit": "Percentage",
+ "minValue": 0,
+ "maxValue": 100,
+ "defaultValue": -1
+ },
+ {
+ "id": "481a7cf8-418b-4774-8fd4-32693ab52299",
+ "name": "power",
+ "displayName": "Power",
+ "displayNameEvent": "Power changed",
+ "displayNameAction": "Set power",
+ "type": "bool",
+ "writable": true,
+ "defaultValue": false,
+ "ioType": "digitalOutput"
+
+ }
+
+
+ ],
+ "actionTypes": [
+
+ ],
+ "eventTypes": [
+
+ ]
+ }
+
+
+ ]
+ }
+ ]
+}
+
+
+
diff --git a/garadget/meta.json b/garadget/meta.json
new file mode 100644
index 00000000..6b3a7ae6
--- /dev/null
+++ b/garadget/meta.json
@@ -0,0 +1,15 @@
+{
+ "title": "Garadget",
+ "tagline": "Garadget",
+ "icon": "garadget.svg",
+ "stability": "community",
+ "offline": true,
+ "technologies": [
+ "network",
+ "mqtt"
+ ],
+ "categories": [
+ "relay",
+ "socket"
+ ]
+}
diff --git a/garadget/translations/068067fe-719b-493b-9cce-0f170978c8f7-en_US.ts b/garadget/translations/068067fe-719b-493b-9cce-0f170978c8f7-en_US.ts
new file mode 100644
index 00000000..d9b0f53a
--- /dev/null
+++ b/garadget/translations/068067fe-719b-493b-9cce-0f170978c8f7-en_US.ts
@@ -0,0 +1,57 @@
+
+
+
+
+ IntegrationPluginGaradget
+
+ Error creating MQTT channel. Please check MQTT server settings.
+ Error setting up thing
+
+
+
+ Could not connect to Garadget device.
+ Error setting up thing
+
+
+
+
+ Garadget
+
+ Sonoff-Garadget
+ The name of the plugin Garadget ({476781af-ad41-423b-b9bb-f308f2aff339})
+
+
+
+ Single switch (Sonoff Basic, RF, Touch...)
+ The name of the ThingClass ({e808b8ae-7608-41ce-8444-892f0648a4d3})
+
+
+
+ Power changed
+ The name of the EventType ({481a7cf8-418b-4774-8fd4-32693ab52299}) of ThingClass sonoff_basic
+
+
+
+ Power
+ The name of the ParamType (ThingClass: sonoff_basic, ActionType: power, ID: {481a7cf8-418b-4774-8fd4-32693ab52299})
+----------
+The name of the ParamType (ThingClass: sonoff_basic, EventType: power, ID: {481a7cf8-418b-4774-8fd4-32693ab52299})
+----------
+The name of the StateType ({481a7cf8-418b-4774-8fd4-32693ab52299}) of ThingClass sonoff_basic
+----------
+The name of the ActionType ({481a7cf8-418b-4774-8fd4-32693ab52299}) of ThingClass sonoff_basic
+
+
+
+ Set power
+ The name of the ActionType ({481a7cf8-418b-4774-8fd4-32693ab52299}) of ThingClass sonoff_basic
+
+
+
+ Garadget
+ The name of the vendor ({22d59bee-79cc-4dea-b742-30ac6bdcf2c9})
+
+
+
+
+
diff --git a/garadget/translations/068067fe-719b-493b-9cce-0f170978c8f7-en_US.ts~ b/garadget/translations/068067fe-719b-493b-9cce-0f170978c8f7-en_US.ts~
new file mode 100644
index 00000000..4bb61265
--- /dev/null
+++ b/garadget/translations/068067fe-719b-493b-9cce-0f170978c8f7-en_US.ts~
@@ -0,0 +1,91 @@
+
+
+
+
+ IntegrationPluginGaradget
+
+ The given IP address is not valid.
+ Error setting up thing
+
+
+
+ Error creating MQTT channel. Please check MQTT server settings.
+ Error setting up thing
+
+
+
+ Could not connect to Garadget device.
+ Error setting up thing
+
+
+
+
+ Garadget
+
+ Sonoff-Garadget
+ The name of the plugin Garadget ({476781af-ad41-423b-b9bb-f308f2aff339})
+
+
+
+ Single switch (Sonoff Basic, RF, Touch...)
+ The name of the ThingClass ({e808b8ae-7608-41ce-8444-892f0648a4d3})
+
+
+
+ IP address
+ The name of the ParamType (ThingClass: sonoff_basic, Type: thing, ID: {54a11a17-fc37-4316-891f-001c55e38220})
+
+
+
+ Connected device
+ The name of the ParamType (ThingClass: sonoff_basic, Type: thing, ID: {f2a79266-1b69-4c50-8397-9533dd2f30cb})
+
+
+
+ Connected changed
+ The name of the EventType ({a3d7e6eb-82d0-47ee-b95f-4dde931eb7e2}) of ThingClass sonoff_basic
+
+
+
+ Connected
+ The name of the ParamType (ThingClass: sonoff_basic, EventType: connected, ID: {a3d7e6eb-82d0-47ee-b95f-4dde931eb7e2})
+----------
+The name of the StateType ({a3d7e6eb-82d0-47ee-b95f-4dde931eb7e2}) of ThingClass sonoff_basic
+
+
+
+
+ Power changed
+ The name of the EventType ({481a7cf8-418b-4774-8fd4-32693ab52299}) of ThingClass sonoff_basic
+
+
+
+ Power
+ The name of the ParamType (ThingClass: sonoff_basic, ActionType: power, ID: {481a7cf8-418b-4774-8fd4-32693ab52299})
+----------
+The name of the ParamType (ThingClass: sonoff_basic, EventType: power, ID: {481a7cf8-418b-4774-8fd4-32693ab52299})
+----------
+The name of the StateType ({481a7cf8-418b-4774-8fd4-32693ab52299}) of ThingClass sonoff_basic
+
+
+
+
+ Garadget
+ The name of the vendor ({22d59bee-79cc-4dea-b742-30ac6bdcf2c9})
+
+
+
+ Signal strength
+ The name of the ParamType (ThingClass: sonoff_basic, EventType: signalStrength, ID: {e276e6ca-4cb5-46d2-9a78-31f92d78ac70})
+----------
+The name of the StateType ({e276e6ca-4cb5-46d2-9a78-31f92d78ac70}) of ThingClass sonoff_basic
+
+
+
+ Signal strength changed
+ The name of the EventType ({e276e6ca-4cb5-46d2-9a78-31f92d78ac70}) of ThingClass sonoff_basic
+
+
+
+
+