diff --git a/debian/nymea-plugin-yeelight.install.in b/debian/nymea-plugin-yeelight.install.in
index 8a70387a..3d7d2733 100644
--- a/debian/nymea-plugin-yeelight.install.in
+++ b/debian/nymea-plugin-yeelight.install.in
@@ -1 +1,3 @@
-usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginws2812fx.so
+yeelight/integrationpluginyeelight.json usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/yeelight/
+yeelight/integrationpluginyeelight.py usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/yeelight/
+yeelight/requirements.txt usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/yeelight/
diff --git a/yeelight/integrationpluginyeelight.cpp b/yeelight/integrationpluginyeelight.cpp
deleted file mode 100644
index 8c7b7923..00000000
--- a/yeelight/integrationpluginyeelight.cpp
+++ /dev/null
@@ -1,362 +0,0 @@
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-*
-* 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 "integrationpluginyeelight.h"
-
-#include "types/param.h"
-#include "plugininfo.h"
-#include "ssdp.h"
-
-#include
-#include
-#include
-
-IntegrationPluginYeelight::IntegrationPluginYeelight()
-{
-
-}
-
-void IntegrationPluginYeelight::init()
-{
- m_connectedStateTypeIds.insert(colorBulbThingClassId, colorBulbConnectedStateTypeId);
- m_connectedStateTypeIds.insert(dimmableBulbThingClassId, dimmableBulbConnectedStateTypeId);
-
- m_powerStateTypeIds.insert(colorBulbThingClassId, colorBulbPowerStateTypeId);
- m_powerStateTypeIds.insert(dimmableBulbThingClassId, dimmableBulbPowerStateTypeId);
-
- m_brightnessStateTypeIds.insert(colorBulbThingClassId, colorBulbBrightnessStateTypeId);
- m_brightnessStateTypeIds.insert(dimmableBulbThingClassId, dimmableBulbBrightnessStateTypeId);
-
- m_ssdp = new Ssdp(this);
- m_ssdp->enable();
- m_ssdp->discover();
-}
-
-void IntegrationPluginYeelight::discoverThings(ThingDiscoveryInfo *info)
-{
- if (info->thingClassId() == colorBulbThingClassId) {
-
-
- qCDebug(dcYeelight()) << "Starting UPnP discovery...";
- /*
- connect(ssdp, &Ssdp::discovered, this,[info, this](const QString &address, int port, int id, const QString &model) {
-
- ThingDescriptor descriptor(colorBulbThingClassId, "Yeelight"+ model + "Bulb", address);
- ParamList params;
- foreach (Thing *existingThing, myThings()) {
- if (existingThing->paramValue(colorBulbThingIdParamTypeId).toString() == id) {
- descriptor.setThingId(existingThing->id());
- break;
- }
- }
- params.append(Param(colorBulbThingHostParamTypeId, address));
- params.append(Param(colorBulbThingIdParamTypeId, id));
- params.append(Param(colorBulbThingPortParamTypeId, port));
- descriptor.setParams(params);
- qCDebug(dcYeelight()) << "UPnP: Found Yeelight thing:" << id;
- info->finish(Thing::ThingErrorNoError);
- });*/
- }
-}
-
-void IntegrationPluginYeelight::setupThing(ThingSetupInfo *info)
-{
- Thing *thing = info->thing();
-
- if (thing->thingClassId() == colorBulbThingClassId) {
- QHostAddress address = QHostAddress(thing->paramValue(colorBulbThingHostParamTypeId).toString());
- quint16 port = thing->paramValue(colorBulbThingPortParamTypeId).toUInt();
- Yeelight *yeelight = new Yeelight(hardwareManager()->networkManager(), address, port, this);
- m_yeelightConnections.insert(thing->id(), yeelight);
- connect(yeelight, &Yeelight::connectionChanged, this, &IntegrationPluginYeelight::onConnectionChanged);
- connect(yeelight, &Yeelight::requestExecuted, this, &IntegrationPluginYeelight::onRequestExecuted);
- connect(yeelight, &Yeelight::colorModeNotificationReceived, this, &IntegrationPluginYeelight::onColorModeNotificationReceived);
- connect(yeelight, &Yeelight::powerNotificationReceived, this, &IntegrationPluginYeelight::onPowerNotificationReceived);
- connect(yeelight, &Yeelight::brightnessNotificationReceived, this, &IntegrationPluginYeelight::onBrightnessNotificationReceived);
- connect(yeelight, &Yeelight::colorTemperatureNotificationReceived, this, &IntegrationPluginYeelight::onColorTemperatureNotificationReceived);
- connect(yeelight, &Yeelight::rgbNotificationReceived, this, &IntegrationPluginYeelight::onRgbNotificationReceived);
- connect(yeelight, &Yeelight::nameNotificationReceived, this, &IntegrationPluginYeelight::onNameNotificationReceived);
- info->finish(Thing::ThingErrorNoError);
- }
-}
-
-void IntegrationPluginYeelight::postSetupThing(Thing *thing)
-{
- connect(thing, &Thing::nameChanged, this, &IntegrationPluginYeelight::onDeviceNameChanged);
-
- if (!m_pluginTimer) {
- m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(60);
- connect(m_pluginTimer, &PluginTimer::timeout, this, [this]() {
- foreach (ThingId id, m_yeelightConnections.keys()) {
- Thing *thing = myThings().findById(id);
- Yeelight *yeelight = m_yeelightConnections.value(id);
- if (yeelight->isConnected()) {
- QList properties;
- if (thing->thingClassId() == colorBulbThingClassId) {
- properties << Yeelight::YeelightProperty::Ct;
- properties << Yeelight::YeelightProperty::Rgb;
- properties << Yeelight::YeelightProperty::Power;
- properties << Yeelight::YeelightProperty::Bright;
- properties << Yeelight::YeelightProperty::ColorMode;
- } else if (thing->thingClassId() == colorBulbThingClassId) {
- properties << Yeelight::YeelightProperty::Power;
- properties << Yeelight::YeelightProperty::Bright;
- }
- yeelight->getParam(properties);
- }
- }
- });
- }
- m_pluginTimer->timeout();
-}
-
-void IntegrationPluginYeelight::executeAction(ThingActionInfo *info)
-{
- Thing *thing = info->thing();
- Action action = info->action();
- Yeelight *yeelight = m_yeelightConnections.value(thing->id());
- if (!yeelight)
- return info->finish(Thing::ThingErrorHardwareFailure);
-
- if (thing->thingClassId() == colorBulbThingClassId) {
-
- if (action.actionTypeId() == colorBulbPowerActionTypeId) {
- bool power = action.param(colorBulbPowerActionPowerParamTypeId).value().toBool();
- int requestId = yeelight->setPower(power);
- connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
- m_asyncActions.insert(requestId, info);
- } else if (action.actionTypeId() == colorBulbBrightnessActionTypeId) {
-
- if (!thing->stateValue(colorBulbPowerStateTypeId).toBool())
- yeelight->setPower(true);
-
- if (m_pendingBrightnessAction.contains(thing->id())) {
- //Scrap old info and insert new one
- m_pendingBrightnessAction.insert(thing->id(), info);
- } else {
- m_pendingBrightnessAction.insert(thing->id(), info);
- QTimer::singleShot(500, this, [yeelight, info, this]{
- int brightness = info->action().param(colorBulbBrightnessActionBrightnessParamTypeId).value().toInt();
- m_pendingBrightnessAction.remove(info->thing()->id());
- int requestId = yeelight->setBrightness(brightness);
- connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
- m_asyncActions.insert(requestId, info);
- });
- }
- } else if (action.actionTypeId() == colorBulbColorActionColorParamTypeId) {
- QRgb color = QColor(action.param(colorBulbColorActionColorParamTypeId).value().toString()).rgba();
- if (!thing->stateValue(colorBulbPowerStateTypeId).toBool())
- yeelight->setPower(true);
- int requestId = yeelight->setRgb(color);
- connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
- m_asyncActions.insert(requestId, info);
- } else if (action.actionTypeId() == colorBulbColorTemperatureActionTypeId) {
- int colorTemperature = 6500 - (action.param(colorBulbColorTemperatureActionColorTemperatureParamTypeId).value().toUInt() * -11.12);
- if (!thing->stateValue(colorBulbPowerStateTypeId).toBool())
- yeelight->setPower(true);
- int requestId = yeelight->setColorTemperature(colorTemperature);
- connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
- m_asyncActions.insert(requestId, info);
- } else if (action.actionTypeId() == colorBulbEffectActionTypeId) {
- QString effect = action.param(colorBulbEffectActionEffectParamTypeId).value().toString();
- if (!thing->stateValue(colorBulbPowerStateTypeId).toBool())
- yeelight->setPower(true);
- if (effect.contains("non")) {
- int requestId = yeelight->stopColorFlow();
- connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
- m_asyncActions.insert(requestId, info);
- } else {
- int requestId = yeelight->startColorFlow();
- connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
- m_asyncActions.insert(requestId, info);
- }
- } else if (action.actionTypeId() == colorBulbAlertActionTypeId) {
- QString alertType = action.param(colorBulbAlertActionAlertParamTypeId).value().toString();
- if (!thing->stateValue(colorBulbPowerStateTypeId).toBool())
- yeelight->setPower(true);
- if (alertType.contains("15")) { //Flash 15 sec
- int requestId = yeelight->flash15s();
- connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
- m_asyncActions.insert(requestId, info);
- } else {
- int requestId = yeelight->flash();
- connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
- m_asyncActions.insert(requestId, info);
- }
- } else if (action.actionTypeId() == colorBulbDefaultActionTypeId) {
- int requestId = yeelight->setDefault();
- connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
- m_asyncActions.insert(requestId, info);
- } else {
- qCWarning(dcYeelight()) << "Unhandled action";
- }
- } else if (thing->thingClassId() == dimmableBulbThingClassId) {
- if (action.actionTypeId() == dimmableBulbPowerActionTypeId) {
- bool power = action.param(dimmableBulbPowerActionPowerParamTypeId).value().toBool();
- int requestId = yeelight->setPower(power);
- connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
- } else if (action.actionTypeId() == dimmableBulbBrightnessActionTypeId) {
- int brightness = action.param(dimmableBulbBrightnessActionBrightnessParamTypeId).value().toInt();
- if (!thing->stateValue(dimmableBulbPowerStateTypeId).toBool())
- yeelight->setPower(true);
- int requestId = yeelight->setBrightness(brightness);
- connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
- m_asyncActions.insert(requestId, info);
- } else if (action.actionTypeId() == dimmableBulbAlertActionTypeId) {
- QString alertType = action.param(dimmableBulbAlertActionAlertParamTypeId).value().toString();
- if (!thing->stateValue(dimmableBulbPowerStateTypeId).toBool())
- yeelight->setPower(true);
- if (alertType.contains("15")) { //Flash 15 sec
- int requestId = yeelight->flash15s();
- connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
- m_asyncActions.insert(requestId, info);
- } else {
- int requestId = yeelight->flash();
- connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
- m_asyncActions.insert(requestId, info);
- }
- } else if (action.actionTypeId() == dimmableBulbDefaultActionTypeId) {
- int requestId = yeelight->setDefault();
- connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
- m_asyncActions.insert(requestId, info);
- } else {
- qCWarning(dcYeelight()) << "Unhandled action";
- }
- } else {
- qCWarning(dcYeelight()) << "Unhandled thing class";
- }
-}
-
-void IntegrationPluginYeelight::thingRemoved(Thing *thing)
-{
- if ((thing->thingClassId() == colorBulbThingClassId) ||
- (thing->thingClassId() == dimmableBulbThingClassId)){
- Yeelight *yeelight = m_yeelightConnections.take(thing->id());
- yeelight->deleteLater();
- }
-}
-
-void IntegrationPluginYeelight::onDeviceNameChanged()
-{
- Thing *thing = static_cast(sender());
- Yeelight *yeelight = m_yeelightConnections.value(thing->id());
- yeelight->setName(thing->name());
-}
-
-void IntegrationPluginYeelight::onConnectionChanged(bool connected)
-{
- Yeelight *yeelight = static_cast(sender());
- Thing * thing = myThings().findById(m_yeelightConnections.key(yeelight));
- if(!thing)
- return;
-
- thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), connected);
-}
-
-void IntegrationPluginYeelight::onRequestExecuted(int requestId, bool success)
-{
- if (m_asyncActions.contains(requestId)) {
- ThingActionInfo *info = m_asyncActions.take(requestId);
- if (success) {
- info->finish(Thing::ThingErrorNoError);
- } else {
- info->finish(Thing::ThingErrorHardwareFailure);
- }
- }
-}
-
-void IntegrationPluginYeelight::onPowerNotificationReceived(bool status)
-{
- Yeelight *yeelight = static_cast(sender());
- Thing * thing = myThings().findById(m_yeelightConnections.key(yeelight));
- if(!thing)
- return;
-
- thing->setStateValue(m_powerStateTypeIds.value(thing->thingClassId()), status);
-}
-
-void IntegrationPluginYeelight::onBrightnessNotificationReceived(int percentage)
-{
- Yeelight *yeelight = static_cast(sender());
- Thing * thing = myThings().findById(m_yeelightConnections.key(yeelight));
- if(!thing)
- return;
-
- thing->setStateValue(m_brightnessStateTypeIds.value(thing->thingClassId()), percentage);
-}
-
-void IntegrationPluginYeelight::onColorTemperatureNotificationReceived(int kelvin)
-{
- Yeelight *yeelight = static_cast(sender());
- Thing * thing = myThings().findById(m_yeelightConnections.key(yeelight));
- if(!thing)
- return;
-
- thing->setStateValue(colorBulbColorTemperatureStateTypeId, kelvin/11.12); //TODO needs proper conversion
-}
-
-void IntegrationPluginYeelight::onRgbNotificationReceived(QRgb rgbColor)
-{
- Yeelight *yeelight = static_cast(sender());
- Thing * thing = myThings().findById(m_yeelightConnections.key(yeelight));
- if(!thing)
- return;
-
- thing->setStateValue(colorBulbColorStateTypeId, rgbColor);
-}
-
-void IntegrationPluginYeelight::onNameNotificationReceived(const QString &name)
-{
- Yeelight *yeelight = static_cast(sender());
- Thing * thing = myThings().findById(m_yeelightConnections.key(yeelight));
- if(!thing)
- return;
- thing->setName(name);
-}
-
-void IntegrationPluginYeelight::onColorModeNotificationReceived(Yeelight::YeelightColorMode colorMode)
-{
- Yeelight *yeelight = static_cast(sender());
- Thing * thing = myThings().findById(m_yeelightConnections.key(yeelight));
- if(!thing)
- return;
-
- switch (colorMode) {
- case Yeelight::RGB:
- thing->setStateValue(colorBulbColorModeStateTypeId, "RGB");
- break;
- case Yeelight::HSV:
- thing->setStateValue(colorBulbColorModeStateTypeId, "HSV");
- break;
- case Yeelight::ColorTemperature:
- thing->setStateValue(colorBulbColorModeStateTypeId, "Color temperature");
- break;
- }
-}
diff --git a/yeelight/integrationpluginyeelight.h b/yeelight/integrationpluginyeelight.h
deleted file mode 100644
index 8045a069..00000000
--- a/yeelight/integrationpluginyeelight.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-*
-* 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 INTEGRATIONPLUGINYEELIGHT_H
-#define INTEGRATIONPLUGINYEELIGHT_H
-
-#include "integrations/integrationplugin.h"
-#include "plugintimer.h"
-#include "network/networkaccessmanager.h"
-#include "network/upnp/upnpdiscovery.h"
-#include "yeelight.h"
-#include "ssdp.h"
-
-#include
-
-class IntegrationPluginYeelight : public IntegrationPlugin
-{
- Q_OBJECT
-
- Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginyeelight.json")
- Q_INTERFACES(IntegrationPlugin)
-
-public:
- explicit IntegrationPluginYeelight();
-
- void init() override;
- void discoverThings(ThingDiscoveryInfo *info) override;
- void setupThing(ThingSetupInfo *info) override;
- void postSetupThing(Thing *thing) override;
- void executeAction(ThingActionInfo *info) override;
- void thingRemoved(Thing *thing) override;
-
-private:
- Ssdp *m_ssdp = nullptr;
- PluginTimer *m_pluginTimer = nullptr;
- QHash m_asyncActions;
- QHash m_yeelightConnections;
-
- QHash m_connectedStateTypeIds;
- QHash m_powerStateTypeIds;
- QHash m_brightnessStateTypeIds;
-
- QHash m_pendingBrightnessAction;
-
-private slots:
- void onDeviceNameChanged();
- void onConnectionChanged(bool connected);
- void onRequestExecuted(int requestId, bool success);
-
- void onPowerNotificationReceived(bool status);
- void onBrightnessNotificationReceived(int percentage);
- void onColorTemperatureNotificationReceived(int kelvin);
- void onRgbNotificationReceived(QRgb rgbColor);
- void onNameNotificationReceived(const QString &name);
- void onColorModeNotificationReceived(Yeelight::YeelightColorMode colorMode);
-};
-
-#endif // INTEGRATIONPLUGINYEELIGHT_H
diff --git a/yeelight/integrationpluginyeelight.py b/yeelight/integrationpluginyeelight.py
new file mode 100644
index 00000000..70a9a000
--- /dev/null
+++ b/yeelight/integrationpluginyeelight.py
@@ -0,0 +1,70 @@
+import nymea
+from yeelight import discover_bulbs
+from yeelight import Bulb
+import threading
+
+timers = {}
+
+def init():
+ logger.log("Yeelight init")
+
+
+def deinit():
+ for timer in timers:
+ timers[timer].cancel()
+
+def discoverThings(info):
+ logger.log("discoverThings", info.thingClassId, "Yeelight discovery test")
+
+def setupThing(info):
+ logger.log("setupThing", info.thing.name, "Setting up bulb")
+ info.thing.nameChangedHandler = nameChanged
+ if info.thingClassId == colorBulbThingClassId:
+ bulb = Bulb("192.168.0.19", effect="smooth", duration=1000)
+
+ timers[info.thing] = threading.Timer(10, runTestForever, [info.thing])
+ timers[info.thing].start()
+ info.finish(nymea.ThingErrorNoError)
+
+def postSetupThing(thing):
+ logger.log("postSetupThing", thing)
+
+
+def nameChanged(thing):
+ logger.log("Thing name changed:", thing.name)
+
+
+def thingRemoved(thing):
+ logger.log("thingRemoved:", thing.name)
+ timers[thing].cancel()
+ del timers[thing]
+
+
+def executeAction(info):
+ logger.log("executeAction")
+ if info.actionTypeId == colorBulbPowerActionTypeId:
+ info.finish(nymea.ThingErrorNoError)
+ runTest(info.thing, duration=info.paramValue(speedtestRunTestActionDurationParamTypeId))
+ bulb.turn_on()
+ bulb.turn_off()
+
+
+def runTest(thing, duration=10):
+ logger.log("Running speed test for:", thing.name, "with maximum duration", duration)
+ thing.setStateValue(speedtestTestRunningStateTypeId, True)
+ result = fast_com(maxtime=duration, verbose=True)
+ thing.setStateValue(speedtestLastResultStateTypeId, result)
+ logger.log("Speed test result:", result)
+ thing.setStateValue(speedtestTestRunningStateTypeId, False)
+ param = nymea.Param(speedtestTestCompletedEventResultParamTypeId, result)
+ params = [param]
+ thing.emitEvent(speedtestTestCompletedEventTypeId, params)
+ logger.log("all done")
+
+
+def runTestForever(thing):
+ del timers[thing]
+ runTest(thing)
+ logger.log("Running next test in %i minutes." % thing.settings[0].value)
+ timers[thing] = threading.Timer(thing.settings[0].value * 60, runTestForever, [thing])
+ timers[thing].start()
diff --git a/yeelight/meta.json b/yeelight/meta.json
new file mode 100644
index 00000000..6eb674cc
--- /dev/null
+++ b/yeelight/meta.json
@@ -0,0 +1,13 @@
+{
+ "title": "Yeelight",
+ "tagline": "Control Yeelight light bulbs.",
+ "icon": "yeelight.png",
+ "stability": "consumer",
+ "offline": true,
+ "technologies": [
+ "network"
+ ],
+ "categories": [
+ "light"
+ ]
+}
diff --git a/yeelight/requirements.txt b/yeelight/requirements.txt
new file mode 100644
index 00000000..8cea8274
--- /dev/null
+++ b/yeelight/requirements.txt
@@ -0,0 +1,15 @@
+
+fastdotcom==0.0.3 \
+ --hash=sha256:84647d665119cfbef01bf785607b85d7a29353208605ca53a4155c331e407cd6 \
+ --hash=sha256:c77689b5595e0ef89d60d5d3a2f669ef2f158d2868fda65e1201d91b53e013b3
+requests==2.24.0 \
+ --hash=sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b \
+ --hash=sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898
+certifi==2017.4.17 \
+ --hash=sha256:f4318671072f030a33c7ca6acaef720ddd50ff124d1388e50c1bda4cbd6d7010
+idna==2.10 \
+ --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0
+chardet==3.0.4 \
+ --hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691
+urllib3==1.25.10 \
+ --hash=sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461
diff --git a/yeelight/yeelight.png b/yeelight/yeelight.png
new file mode 100644
index 00000000..ad4270a5
Binary files /dev/null and b/yeelight/yeelight.png differ
diff --git a/yeelight/yeelight.pro b/yeelight/yeelight.pro
index 6fd79b34..ecd12541 100644
--- a/yeelight/yeelight.pro
+++ b/yeelight/yeelight.pro
@@ -1,15 +1,4 @@
-include(../plugins.pri)
+TEMPLATE = aux
-QT += network
-
-TARGET = $$qtLibraryTarget(nymea_integrationpluginyeelight)
-
-SOURCES += \
- integrationpluginyeelight.cpp \
- yeelight.cpp \
- ssdp.cpp \
-
-HEADERS += \
- integrationpluginyeelight.h \
- yeelight.h \
- ssdp.h \
+OTHER_FILES = integrationpluginyeelight.py \
+ integrationpluginyeelight.json