From b9a46d21bbcbc9a05baa38976912623b44002efd Mon Sep 17 00:00:00 2001 From: trinnes Date: Tue, 30 Apr 2024 08:23:41 +0200 Subject: [PATCH] Add Logilink plug-in --- debian/control | 10 + debian/nymea-plugin-logilink.install.in | 2 + logilink/README.md | 22 ++ logilink/integrationpluginlogilink.cpp | 288 ++++++++++++++++++ logilink/integrationpluginlogilink.h | 69 +++++ logilink/integrationpluginlogilink.json | 116 +++++++ logilink/logilink.png | Bin 0 -> 1645 bytes logilink/logilink.pro | 11 + logilink/meta.json | 14 + ...0496e-06b5-11ef-8f86-2be8f966eb8d-en_US.ts | 111 +++++++ nymea-plugins.pro | 1 + 11 files changed, 644 insertions(+) create mode 100644 debian/nymea-plugin-logilink.install.in create mode 100644 logilink/README.md create mode 100644 logilink/integrationpluginlogilink.cpp create mode 100644 logilink/integrationpluginlogilink.h create mode 100644 logilink/integrationpluginlogilink.json create mode 100644 logilink/logilink.png create mode 100644 logilink/logilink.pro create mode 100644 logilink/meta.json create mode 100644 logilink/translations/3e80496e-06b5-11ef-8f86-2be8f966eb8d-en_US.ts diff --git a/debian/control b/debian/control index 49ef4ffd..3e08f32e 100644 --- a/debian/control +++ b/debian/control @@ -352,6 +352,16 @@ Conflicts: nymea-plugins-translations (< 1.0.1) Description: nymea integration plugin for lifx This package contains the nymea integration plugin for lifx devices +Package: nymea-plugin-logilink +Architecture: any +Section: libs +Depends: ${shlibs:Depends}, + ${misc:Depends}, +Conflicts: nymea-plugins-translations (< 1.0.1) +Description: nymea integration plugin for Logilink power sockets + This package contains the nymea integration plugin for Logilink PDU + network controlled power sockets. + Package: nymea-plugin-mecelectronics Architecture: any Depends: ${shlibs:Depends}, diff --git a/debian/nymea-plugin-logilink.install.in b/debian/nymea-plugin-logilink.install.in new file mode 100644 index 00000000..fcf460cb --- /dev/null +++ b/debian/nymea-plugin-logilink.install.in @@ -0,0 +1,2 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginlogilink.so +logilink/translations/*qm usr/share/nymea/translations/ diff --git a/logilink/README.md b/logilink/README.md new file mode 100644 index 00000000..00531636 --- /dev/null +++ b/logilink/README.md @@ -0,0 +1,22 @@ +# Logilink + +This integration allows nymea to control Logilink power sockets. + +## Supported things + +* PDU8P01 + +* Secure connection with username and password +* Get and set the state of each socket +* No internet or cloud connection required + +## Requirements + +* The Logilink device must be in the same local area network as nymea. +* TCP Sockets on port 80 must not be blocked by the router. +* Access to the device login credentials. +* The package “nymea-plugin-logilink” must be installed + +## More + +See https://logilink.de for a detailed description of the devices. diff --git a/logilink/integrationpluginlogilink.cpp b/logilink/integrationpluginlogilink.cpp new file mode 100644 index 00000000..c5058ada --- /dev/null +++ b/logilink/integrationpluginlogilink.cpp @@ -0,0 +1,288 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2024, 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 "integrationpluginlogilink.h" +#include "plugininfo.h" +#include "plugintimer.h" + +#include +#include +#include +#include +#include + +IntegrationPluginLogilink::IntegrationPluginLogilink() +{ +} + +IntegrationPluginLogilink::~IntegrationPluginLogilink() +{ + m_pollTimer->deleteLater(); +} + +void IntegrationPluginLogilink::startPairing(ThingPairingInfo *info) +{ + info->finish(Thing::ThingErrorNoError, QT_TR_NOOP("Please enter the login credentials for your device.")); +} + +void IntegrationPluginLogilink::init() +{ + m_pollTimer = hardwareManager()->pluginTimerManager()->registerTimer(m_pollInterval); + connect(m_pollTimer, &PluginTimer::timeout, this, &IntegrationPluginLogilink::refreshStates); +} + +void IntegrationPluginLogilink::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &password) +{ + if (info->thingClassId() == pdu8p01ThingClassId) { + QString ipAddress = info->params().paramValue(pdu8p01ThingIpv4AddressParamTypeId).toString(); + + QNetworkRequest request; + request.setUrl(QUrl(QString("http://%1/status.xml").arg(ipAddress))); + request.setRawHeader("Authorization", "Basic " + QString("%1:%2").arg(username).arg(password).toUtf8().toBase64()); + qCDebug(dcLogilink()) << "ConfirmPairing fetching:" << request.url() << request.rawHeader("Authorization"); + QNetworkReply *reply = hardwareManager()->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, info, [=](){ + if (reply->error() == QNetworkReply::NoError) { + pluginStorage()->beginGroup(info->thingId().toString()); + pluginStorage()->setValue("username", username); + pluginStorage()->setValue("password", password); + pluginStorage()->endGroup(); + info->finish(Thing::ThingErrorNoError); + } else { + info->finish(Thing::ThingErrorAuthenticationFailure, QT_TR_NOOP("Wrong username or password.")); + } + }); + } else { + qCWarning(dcLogilink()) << "Unhandled ThingClass in confirmPairing" << info->thingClassId(); + info->finish(Thing::ThingErrorThingClassNotFound); + } +} + +void IntegrationPluginLogilink::setupThing(ThingSetupInfo *info) +{ + Thing *thing = info->thing(); + qCDebug(dcLogilink()) << "Setup" << thing->name(); + + if (thing->thingClassId() == pdu8p01ThingClassId) { + if (!m_pollTimer->running()) { + m_pollTimer->start(); + } + + QString ipAddress = thing->paramValue(pdu8p01ThingIpv4AddressParamTypeId).toString(); + + QNetworkRequest request; + pluginStorage()->beginGroup(thing->id().toString()); + const QString username = pluginStorage()->value("username").toString(); + const QString password = pluginStorage()->value("password").toString(); + pluginStorage()->endGroup(); + + request.setUrl(QUrl(QString("http://%1/status.xml").arg(ipAddress))); + request.setRawHeader("Authorization", "Basic " + QString("%1:%2").arg(username).arg(password).toUtf8().toBase64()); + QNetworkReply *reply = hardwareManager()->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, info, [=](){ + if (reply->error() == QNetworkReply::NoError) { + info->finish(Thing::ThingErrorNoError); + + if (myThings().filterByParentId(thing->id()).isEmpty()) { + // Creating sockets as child 'things' + ThingDescriptors descriptorList; + for (int i = 0; i < 8; i++) { + QString deviceName = thing->name() + " socket " + QString::number(i); + ThingDescriptor d(socketThingClassId, deviceName, thing->name(), thing->id()); + d.setParams(ParamList() << Param(socketThingNumberParamTypeId, i)); + descriptorList << d; + } + emit autoThingsAppeared(descriptorList); + } + } else if (reply->error() == QNetworkReply::AuthenticationRequiredError) { + info->finish(Thing::ThingErrorAuthenticationFailure, QT_TR_NOOP("Wrong username or password")); + } else { + info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("Device not found")); + } + }); + + return; + } + + if (thing->thingClassId() == socketThingClassId) { + info->finish(Thing::ThingErrorNoError); + return; + } + + qCWarning(dcLogilink()) << "Unhandled ThingClass in setupDevice" << thing->thingClassId(); + info->finish(Thing::ThingErrorThingClassNotFound); +} + +void IntegrationPluginLogilink::postSetupThing(Thing *thing) +{ + qCDebug(dcLogilink()) << "Post setup" << thing->name(); + if (thing->thingClassId() == pdu8p01ThingClassId) { + getStates(thing); + } +} + +void IntegrationPluginLogilink::thingRemoved(Thing *thing) +{ + qCDebug(dcLogilink()) << "Thing removed" << thing->name(); + if (thing->thingClassId() == pdu8p01ThingClassId) { + pluginStorage()->remove(thing->id().toString()); + } + + if (myThings().filterByThingClassId(pdu8p01ThingClassId).isEmpty()) { + m_pollTimer->stop(); + } +} + +void IntegrationPluginLogilink::executeAction(ThingActionInfo *info) +{ + Thing *thing = info->thing(); + Action action = info->action(); + + if (thing->thingClassId() == socketThingClassId) { + if (action.actionTypeId() == socketPowerActionTypeId) { + + Thing *parentDevice = myThings().findById(thing->parentId()); + auto ipAddress = parentDevice->paramValue(pdu8p01ThingIpv4AddressParamTypeId).toString(); + pluginStorage()->beginGroup(parentDevice->id().toString()); + QString username = pluginStorage()->value("username").toString(); + QString password = pluginStorage()->value("password").toString(); + pluginStorage()->endGroup(); + + QUrl url(QString("http://%1/control_outlet.htm").arg(ipAddress)); + QUrlQuery query; + query.addQueryItem("outlet" + thing->paramValue(socketThingNumberParamTypeId).toString(), "1"); + query.addQueryItem("op", action.param(socketPowerActionPowerParamTypeId).value().toBool() ? "0" : "1"); // op code 0 is on, 1 is off + query.addQueryItem("submit", "Apply"); + url.setQuery(query); + QNetworkRequest request(url); + request.setRawHeader("Authorization", "Basic " + QString("%1:%2").arg(username, password).toUtf8().toBase64()); + + QNetworkReply *reply = hardwareManager()->networkManager()->get(request); + qCDebug(dcLogilink()) << "Requesting:" << url.toString() << request.rawHeader("Authorization"); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, info, [reply, info](){ + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcLogilink()) << "Execute action failed:" << reply->error() << reply->errorString(); + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + info->finish(Thing::ThingErrorNoError); + }); + return; + } + info->finish(Thing::ThingErrorActionTypeNotFound); + } + info->finish(Thing::ThingErrorThingClassNotFound); +} + +void IntegrationPluginLogilink::refreshStates() +{ + foreach (Thing *thing, myThings().filterByThingClassId(pdu8p01ThingClassId)) { + getStates(thing); + } +} + +void IntegrationPluginLogilink::getStates(Thing *thing) +{ + auto ipAddress = thing->paramValue(pdu8p01ThingIpv4AddressParamTypeId).toString(); + pluginStorage()->beginGroup(thing->id().toString()); + QString username = pluginStorage()->value("username").toString(); + QString password = pluginStorage()->value("password").toString(); + pluginStorage()->endGroup(); + + QUrl url(QString("http://%1/status.xml").arg(ipAddress)); + + QNetworkRequest request; + request.setUrl(url); + request.setRawHeader("Authorization", "Basic " + QString("%1:%2").arg(username, password).toUtf8().toBase64()); + QNetworkReply *reply = hardwareManager()->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, thing, [this, thing, reply](){ + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcLogilink()) << "Error fetching stats for" << thing->name() << reply->errorString(); + thing->setStateValue(pdu8p01ConnectedStateTypeId, false); + foreach (auto child, myThings().filterByParentId(thing->id())) { + child->setStateValue(socketConnectedStateTypeId, false); + } + return; + } + QXmlStreamReader xml; + xml.addData(reply->readAll()); + if (xml.hasError()) { + qCDebug(dcLogilink()) << "XML Error:" << xml.errorString(); + return; + } + thing->setStateValue(pdu8p01ConnectedStateTypeId, true); + if (xml.readNextStartElement()) { + if (xml.name() == "response") { + qCDebug(dcLogilink()) << "XML contains response"; + } else { + qCWarning(dcLogilink()) << "xml name" << xml.name(); + } + while(xml.readNextStartElement()) { + qCDebug(dcLogilink()) << "XML name" << xml.name(); + if (xml.name() == "curBan") { + auto current = xml.readElementText().toDouble(); + qCDebug(dcLogilink()) << "Current" << current; + thing->setStateValue(pdu8p01TotalLoadStateTypeId, current); + } else if (xml.name() == "statBan") { + auto status = xml.readElementText(); + qCDebug(dcLogilink()) << "Status" << status; + thing->setStateValue(pdu8p01StatusStateTypeId, status); + } else if (xml.name() == "tempBan") { + auto temperature = xml.readElementText().toDouble(); + qCDebug(dcLogilink()) << "Temperature" << temperature; + thing->setStateValue(pdu8p01TemperatureStateTypeId, temperature); + } else if (xml.name() == "humBan") { + auto humidity = xml.readElementText().toDouble(); + qCDebug(dcLogilink()) << "hummidity" << humidity; + thing->setStateValue(pdu8p01HumidityStateTypeId, humidity); + } else if (xml.name().startsWith("outletStat")){ + int socketNumber = xml.name().right(1).toInt(); + bool socketValue = xml.readElementText().startsWith("on"); + auto socketThing = myThings().filterByParentId(thing->id()) + .filterByParam(socketThingNumberParamTypeId, socketNumber) + .first(); + if (!socketThing) { + // Socket not yet setup + continue; + } + qCDebug(dcLogilink()) << "Socket" << socketNumber << socketValue; + socketThing->setStateValue(socketPowerStateTypeId, socketValue); + socketThing->setStateValue(socketConnectedStateTypeId, true); + } else { + xml.skipCurrentElement(); + } + } + } + }); +} diff --git a/logilink/integrationpluginlogilink.h b/logilink/integrationpluginlogilink.h new file mode 100644 index 00000000..5d77ad79 --- /dev/null +++ b/logilink/integrationpluginlogilink.h @@ -0,0 +1,69 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2024, 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 INTEGRATIONPLUGINLOGILINK_H +#define INTEGRATIONPLUGINLOGILINK_H + +#include "integrations/integrationplugin.h" + +#include + +class PluginTimer; + +class IntegrationPluginLogilink: public IntegrationPlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginlogilink.json") + Q_INTERFACES(IntegrationPlugin) + +public: + explicit IntegrationPluginLogilink(); + ~IntegrationPluginLogilink(); + + void startPairing(ThingPairingInfo *info) override; + void init() override; + void confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) override; + void setupThing(ThingSetupInfo *info) override; + void postSetupThing(Thing *thing) override; + void thingRemoved(Thing *thing) override; + void executeAction(ThingActionInfo *info) override; + +private slots: + void refreshStates(); + +private: + PluginTimer *m_pollTimer = nullptr; + const int m_pollInterval = 1; // seconds + + void getStates(Thing *thing); +}; + +#endif // INTEGRATIONPLUGINLOGILINK_H diff --git a/logilink/integrationpluginlogilink.json b/logilink/integrationpluginlogilink.json new file mode 100644 index 00000000..1a611e3a --- /dev/null +++ b/logilink/integrationpluginlogilink.json @@ -0,0 +1,116 @@ +{ + "name": "logilink", + "displayName": "Logilink", + "id": "3e80496e-06b5-11ef-8f86-2be8f966eb8d", + "vendors": [ + { + "name": "logilink", + "displayName": "Logilink", + "id": "4aed2c3a-06b5-11ef-8a6b-ff770ac74f51", + "thingClasses": [ + { + "id": "d8162d6e-06b5-11ef-888f-13a1e6eca98c", + "name": "pdu8p01", + "displayName": "PDU8P01", + "createMethods": ["user"], + "setupMethod": "userandpassword", + "interfaces": [ "gateway", "temperaturesensor", "humiditysensor"], + "paramTypes": [ + { + "id": "d2e1a63e-06b5-11ef-bf2d-5f0d67f222af", + "name":"ipv4Address", + "displayName": "IPv4 address", + "type": "QString", + "inputType": "IPv4Address", + "defaultValue": "192.168.0.100" + } + ], + "stateTypes": [ + { + "id": "c5c7f1ba-06b5-11ef-ad08-03562210396e", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false, + "cached": false + }, + { + "id": "413af8c0-0796-11ef-bd2b-4fde50f95418", + "name": "totalLoad", + "displayName": "Total load", + "displayNameEvent": "Total load changed", + "type": "double", + "defaultValue": 0, + "unit": "Ampere" + }, + { + "id": "c0ce082a-06b5-11ef-b71a-7f0e8b9a48c0", + "name": "temperature", + "displayName": "Temperature", + "displayNameEvent": "Temperature changed", + "type": "double", + "defaultValue": 0, + "unit": "DegreeCelsius" + }, + { + "id": "1aaaf3ca-0793-11ef-8b3e-f7eff48006c2", + "name": "humidity", + "displayName": "Humidity", + "displayNameEvent": "Humidity changed", + "type": "double", + "defaultValue": 0, + "minValue": 0, + "maxValue": 100, + "unit": "Percentage" + }, + { + "id": "6ec71e40-0796-11ef-a8f8-a33a504a66bb", + "name": "status", + "displayName": "Status", + "displayNameEvent": "Status changed", + "type": "QString", + "defaultValue": "Unknown" + } + ] + }, + { + "id": "a320bfb6-06b5-11ef-81e4-834aeaf5fe45", + "name": "socket", + "displayName": "Logilink Socket", + "createMethods": ["auto"], + "interfaces": ["powersocket", "connectable"], + "paramTypes": [ + { + "id": "b329edba-06b5-11ef-b04e-d3622c766dd0", + "name": "number", + "displayName": "Socket number", + "type": "int" + } + ], + "stateTypes": [ + { + "id": "b8d9679a-06b5-11ef-9812-fb840afffc88", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "47329958-c33f-478f-b2a0-910abd150da8", + "name": "power", + "displayName": "Power", + "displayNameEvent": "Power changed", + "displayNameAction": "Set power", + "writable": true, + "type": "bool", + "defaultValue": false, + "ioType": "digitalOutput" + } + ] + } + ] + } + ] +} diff --git a/logilink/logilink.png b/logilink/logilink.png new file mode 100644 index 0000000000000000000000000000000000000000..24d65dd65b9777428d108d9e300ad064e9f88183 GIT binary patch literal 1645 zcmV-z29o)SP)zxPNT0+x{eNT<_DRZ0*9K@bE%5ClOG1VQM|x7ZmFZh$2H%zrLQXFk;1(1QI!XGscJA%zo8{VZ%hx9_H^leY84!fA5s?eCM?@~n9uc`P zdw%r)iW_H21G_2ZWkN_xF3cVgxiEX;n=45QlLYJhSm~q8DL;<+k>y?SX`pmoRCQtk z*yhJMPvaQ7I3@-9^=Z9;Pya*Vgw7>)7F~!Z@+7^>f4(aeo{gnG;Cc|?)_dXv(}4+? zmxil$`r5NX!9%|8N`K$v>3&$g|Cg$Y0A9$)(8cMUm!?@re$vXY(0|+I*Fk;kM|>VG zA5ce+K01hFt|Tc47Ycdpao75KPm->j@4$3^t7d_hfeJjRs=#jt=&0s!?wdjc1Bx=x29Nufb|Cuche90)6NG#w7i zUyPaX^XdjOhq0CG9JGj=u(inWo%bZ2XMO%l*q9>$LM@jmn1|0yIdNkn*OtE>Y`~~S zE;=urVthi| zf?M{mz~J+N6?l~`bIxacRR{IK^}f9X4umvUxWV7n<9^qIg>+~^?Im2em7_XXO@zOGiXL4t+f-+${8V4AU}{3z3TN(j)HSOunzVo0I|P z6oGLQt+(@7Ap)_Y5Qm&}Sy(;`(}io87J5Epa3s`Lm=fzTVqrw&LM*`X@XVYaJQgOF z$cK#bJ6k>r(*~w5- zFT#i%Cvm9IyJVgU_EMQ3Lf<>>`g#|V5CFS<{_bRQLPRR8CSoE}Lk>J?49U`SDVte{ z-1MCQZOs>YDU~boQpAF{&ut5BkYdT6X#khk-%Bq>NRe~XwLXgzxCiA#&ZpDnmcqh1 zc;-nM9?m{r>M#HfI1ew!LZQ&%-i<2NV>pqc-9-gx-nwQ_3yP15G_$N;;nIQc|9vee z+V#QU1_OnB7gh^nV7Jd-Ydta!OrhlmVS`n$SGZsXe$ULn)g^WXR>*hFGF{rX)tRX- zox(IAgVnt}Fq7OqDdugp!KI^g0lfBjo-r7*eCGk!=cotjT><<2R2qjt{g&-`XrwSM zDoHbQf7r<&6fif-W%fq-bKOYZ9Q>M4rP`LV73uiK$h5sYLMV9q9H0|L^m9hfhoAAp zw-8#Nu0dLED@HJ#2jw2Oer&k@mRIq|glnkE6Gc#0h4TVmj74eZuWMb#nbt> z?Z7?v-b?1NJBPyJ+*441+iPB6v9!H+5Ru2gZlB{s5xO1Cfb&7$V_uAf*@YqgR=Qv+w%%I-=iu%EHBCA5Hcx)FG# z=Z(Ls?ewU-DuwBT!e-4%i6<|^Z-68s|KiNEB` + + + + IntegrationPluginLogilink + + + Please enter the login credentials for your device. + + + + + Wrong username or password. + + + + + Wrong username or password + + + + + Device not found + + + + + logilink + + + + Connected + The name of the StateType ({b8d9679a-06b5-11ef-9812-fb840afffc88}) of ThingClass socket +---------- +The name of the StateType ({c5c7f1ba-06b5-11ef-ad08-03562210396e}) of ThingClass pdu8p01 + + + + + Humidity + The name of the StateType ({1aaaf3ca-0793-11ef-8b3e-f7eff48006c2}) of ThingClass pdu8p01 + + + + + IPv4 address + The name of the ParamType (ThingClass: pdu8p01, Type: thing, ID: {d2e1a63e-06b5-11ef-bf2d-5f0d67f222af}) + + + + + + Logilink + The name of the vendor ({4aed2c3a-06b5-11ef-8a6b-ff770ac74f51}) +---------- +The name of the plugin logilink ({3e80496e-06b5-11ef-8f86-2be8f966eb8d}) + + + + + Logilink Socket + The name of the ThingClass ({a320bfb6-06b5-11ef-81e4-834aeaf5fe45}) + + + + + PDU8P01 + The name of the ThingClass ({d8162d6e-06b5-11ef-888f-13a1e6eca98c}) + + + + + + Power + The name of the ParamType (ThingClass: socket, ActionType: power, ID: {47329958-c33f-478f-b2a0-910abd150da8}) +---------- +The name of the StateType ({47329958-c33f-478f-b2a0-910abd150da8}) of ThingClass socket + + + + + Set power + The name of the ActionType ({47329958-c33f-478f-b2a0-910abd150da8}) of ThingClass socket + + + + + Socket number + The name of the ParamType (ThingClass: socket, Type: thing, ID: {b329edba-06b5-11ef-b04e-d3622c766dd0}) + + + + + Status + The name of the StateType ({6ec71e40-0796-11ef-a8f8-a33a504a66bb}) of ThingClass pdu8p01 + + + + + Temperature + The name of the StateType ({c0ce082a-06b5-11ef-b71a-7f0e8b9a48c0}) of ThingClass pdu8p01 + + + + + Total load + The name of the StateType ({413af8c0-0796-11ef-bd2b-4fde50f95418}) of ThingClass pdu8p01 + + + + diff --git a/nymea-plugins.pro b/nymea-plugins.pro index 6e9da803..9f4b53b0 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -37,6 +37,7 @@ PLUGIN_DIRS = \ kodi \ lgsmarttv \ lifx \ + logilink \ mecelectronics \ meross \ mailnotification \