WIP: New Plugin: Nymea owlets

This commit is contained in:
Michael Zanetti 2021-10-16 10:29:22 +02:00
parent 2bb91cf98b
commit 3985f2ad5a
4 changed files with 531 additions and 0 deletions

View File

@ -0,0 +1,239 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, 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 <https://www.gnu.org/licenses/>.
*
* 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 "integrationpluginowlet.h"
#include "plugininfo.h"
#include "owletclient.h"
#include "hardwaremanager.h"
#include "platform/platformzeroconfcontroller.h"
#include "network/zeroconf/zeroconfservicebrowser.h"
#include "network/zeroconf/zeroconfserviceentry.h"
#include <QColor>
#include <QTimer>
static QHash<ThingClassId, ParamTypeId> idParamTypeMap = {
{ digitalOutputThingClassId, digitalOutputThingOwletIdParamTypeId },
{ digitalInputThingClassId, digitalInputThingOwletIdParamTypeId },
{ ws2812ThingClassId, ws2812ThingOwletIdParamTypeId }
};
IntegrationPluginOwlet::IntegrationPluginOwlet()
{
}
void IntegrationPluginOwlet::init()
{
m_zeroConfBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_nymea-owlet._tcp");
}
void IntegrationPluginOwlet::discoverThings(ThingDiscoveryInfo *info)
{
foreach (const ZeroConfServiceEntry &entry, m_zeroConfBrowser->serviceEntries()) {
qCDebug(dcOwlet()) << "Found owlet:" << entry;
ThingDescriptor descriptor(info->thingClassId(), entry.name(), entry.txt("platform"));
descriptor.setParams(ParamList() << Param(idParamTypeMap.value(info->thingClassId()), entry.txt("id")));
foreach (Thing *existingThing, myThings().filterByParam(idParamTypeMap.value(info->thingClassId()), entry.txt("id"))) {
descriptor.setThingId(existingThing->id());
break;
}
info->addThingDescriptor(descriptor);
}
info->finish(Thing::ThingErrorNoError);
}
void IntegrationPluginOwlet::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
QHostAddress ip;
int port = 5555;
foreach (const ZeroConfServiceEntry &entry, m_zeroConfBrowser->serviceEntries()) {
if (entry.txt("id") == info->thing()->paramValue(idParamTypeMap.value(info->thing()->thingClassId()))) {
ip = entry.hostAddress();
port = entry.port();
break;
}
}
// Try cached ip
if (ip.isNull()) {
pluginStorage()->beginGroup(thing->id().toString());
ip = QHostAddress(pluginStorage()->value("cachedIP").toString());
pluginStorage()->endGroup();
}
if (ip.isNull()) {
qCWarning(dcOwlet()) << "Can't find owlet in the local network.";
info->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
OwletClient *client = new OwletClient(this);
connect(client, &OwletClient::connected, info, [=](){
qCDebug(dcOwlet()) << "Connected to owleet";
m_clients.insert(thing, client);
if (thing->thingClassId() == digitalOutputThingClassId) {
QVariantMap params;
params.insert("id", thing->paramValue(digitalOutputThingPinParamTypeId).toInt());
params.insert("mode", "GPIOOutput");
client->sendCommand("GPIO.ConfigurePin", params);
}
if (thing->thingClassId() == digitalInputThingClassId) {
QVariantMap params;
params.insert("id", thing->paramValue(digitalInputThingPinParamTypeId).toInt());
params.insert("mode", "GPIOInput");
client->sendCommand("GPIO.ConfigurePin", params);
}
if (thing->thingClassId() == ws2812ThingClassId) {
QVariantMap params;
params.insert("id", thing->paramValue(ws2812ThingPinParamTypeId).toInt());
params.insert("mode", "WS2812");
params.insert("ledCount", thing->paramValue(ws2812ThingLedCountParamTypeId).toUInt());
params.insert("ledMode", "WS2812Mode" + thing->paramValue(ws2812ThingLedModeParamTypeId).toString());
params.insert("ledClock", "WS2812Clock" + thing->paramValue(ws2812ThingLedClockParamTypeId).toString());
client->sendCommand("GPIO.ConfigurePin", params);
}
info->finish(Thing::ThingErrorNoError);
});
connect(client, &OwletClient::error, info, [=](){
info->finish(Thing::ThingErrorHardwareFailure);
});
connect(client, &OwletClient::connected, thing, [=](){
thing->setStateValue("connected", true);
pluginStorage()->beginGroup(thing->id().toString());
pluginStorage()->setValue("cachedIP", ip.toString());
pluginStorage()->endGroup();
});
connect(client, &OwletClient::disconnected, thing, [=](){
thing->setStateValue("connected", false);
});
connect(client, &OwletClient::notificationReceived, this, [=](const QString &name, const QVariantMap &params){
qCDebug(dcOwlet()) << "***Notif" << name << params;
if (thing->thingClassId() == digitalInputThingClassId) {
if (params.value("id").toInt() == thing->paramValue(digitalInputThingPinParamTypeId)) {
thing->setStateValue(digitalInputPowerStateTypeId, params.value("power").toBool());
}
}
if (thing->thingClassId() == digitalOutputThingClassId) {
if (params.value("id").toInt() == thing->paramValue(digitalOutputThingPinParamTypeId)) {
thing->setStateValue(digitalOutputPowerStateTypeId, params.value("power").toBool());
}
}
if (thing->thingClassId() == ws2812ThingClassId) {
if (name == "GPIO.PinChanged") {
if (params.contains("power")) {
thing->setStateValue(ws2812PowerStateTypeId, params.value("power").toBool());
}
if (params.contains("brightness")) {
thing->setStateValue(ws2812BrightnessStateTypeId, params.value("brightness").toInt());
}
if (params.contains("color")) {
thing->setStateValue(ws2812ColorStateTypeId, params.value("color").value<QColor>());
}
}
}
});
client->connectToHost(ip, port);
}
void IntegrationPluginOwlet::executeAction(ThingActionInfo *info)
{
if (info->thing()->thingClassId() == digitalOutputThingClassId) {
OwletClient *client = m_clients.value(info->thing());
QVariantMap params;
params.insert("id", info->thing()->paramValue(digitalOutputThingPinParamTypeId).toInt());
params.insert("power", info->action().paramValue(digitalOutputPowerActionPowerParamTypeId).toBool());
qCDebug(dcOwlet()) << "Sending ControlPin" << params;
int id = client->sendCommand("GPIO.ControlPin", params);
connect(client, &OwletClient::replyReceived, info, [=](int commandId, const QVariantMap &params){
if (id != commandId) {
return;
}
qCDebug(dcOwlet()) << "reply from owlet:" << params;
QString error = params.value("error").toString();
if (error == "GPIOErrorNoError") {
info->thing()->setStateValue(digitalOutputPowerStateTypeId, info->action().paramValue(digitalOutputPowerActionPowerParamTypeId).toBool());
info->finish(Thing::ThingErrorNoError);
} else {
info->finish(Thing::ThingErrorHardwareFailure);
}
});
return;
}
if (info->thing()->thingClassId() == ws2812ThingClassId) {
OwletClient *client = m_clients.value(info->thing());
QVariantMap params;
params.insert("id", info->thing()->paramValue(ws2812ThingPinParamTypeId).toUInt());
if (info->action().actionTypeId() == ws2812PowerActionTypeId) {
params.insert("power", info->action().paramValue(ws2812PowerActionPowerParamTypeId).toBool());
}
if (info->action().actionTypeId() == ws2812BrightnessActionTypeId) {
params.insert("brightness", info->action().paramValue(ws2812BrightnessActionBrightnessParamTypeId).toInt());
}
if (info->action().actionTypeId() == ws2812ColorActionTypeId) {
QColor color = info->action().paramValue(ws2812ColorActionColorParamTypeId).value<QColor>();
params.insert("color", (color.rgb() & 0xFFFFFF));
}
int id = client->sendCommand("GPIO.ControlPin", params);
connect(client, &OwletClient::replyReceived, info, [=](int commandId, const QVariantMap &params){
if (id != commandId) {
return;
}
qCDebug(dcOwlet()) << "reply from owlet:" << params;
QString error = params.value("error").toString();
if (error == "GPIOErrorNoError") {
info->finish(Thing::ThingErrorNoError);
} else {
info->finish(Thing::ThingErrorHardwareFailure);
}
});
return;
}
Q_ASSERT_X(false, "IntegrationPluginOwlet", "Not implemented");
info->finish(Thing::ThingErrorUnsupportedFeature);
}
void IntegrationPluginOwlet::thingRemoved(Thing *thing)
{
Q_UNUSED(thing)
}

View File

@ -0,0 +1,63 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, 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 <https://www.gnu.org/licenses/>.
*
* 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 INTEGRATIONPLUGINOWLET_H
#define INTEGRATIONPLUGINOWLET_H
#include "integrations/integrationplugin.h"
#include "extern-plugininfo.h"
class ZeroConfServiceBrowser;
class OwletClient;
class IntegrationPluginOwlet: public IntegrationPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginowlet.json")
Q_INTERFACES(IntegrationPlugin)
public:
explicit IntegrationPluginOwlet();
void init() override;
void discoverThings(ThingDiscoveryInfo *info) override;
void setupThing(ThingSetupInfo *info) override;
void executeAction(ThingActionInfo *info) override;
void thingRemoved(Thing *thing) override;
private:
ZeroConfServiceBrowser *m_zeroConfBrowser = nullptr;
QHash<Thing*, OwletClient*> m_clients;
};
#endif // INTEGRATIONPLUGINOWLET_H

View File

@ -0,0 +1,216 @@
{
"displayName": "nymea owlet",
"name": "owlet",
"id": "699a5b6d-d90f-4554-a8de-9205768a4a98",
"vendors": [
{
"displayName": "nymea GmbH",
"name": "nymea",
"id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6",
"thingClasses": [
{
"id": "5a079c4e-9309-4d98-9ff1-9beeda210958",
"displayName": "Digital GPIO output on owlet",
"name": "digitalOutput",
"createMethods": ["discovery"],
"interfaces": ["power"],
"paramTypes": [
{
"id": "de8cda8f-b8f1-425d-ae16-fd0f5a885ca4",
"name": "owletId",
"displayName": "Owlet ID",
"type": "QString",
"defaultValue": ""
},
{
"id": "31dbcdea-04f3-4a0c-b131-7eda8a92c602",
"name": "pin",
"displayName": "Pin number",
"type": "uint",
"defaultValue": 1
}
],
"stateTypes": [
{
"id": "dd97a6b1-e98e-4a60-b16a-b27240b91439",
"name": "power",
"displayName": "Power",
"type": "bool",
"defaultValue": false,
"writable": true,
"displayNameEvent": "Power changed",
"displayNameAction": "Set power",
"ioType": "digitalOutput"
}
]
},
{
"id": "673512a3-75d8-44a6-9930-198c9f1a1f03",
"displayName": "Digital GPIO Input on owlet",
"name": "digitalInput",
"createMethods": ["discovery"],
"paramTypes": [
{
"id": "dd7eca3f-13f6-4320-aaaa-b0be8fbfeebf",
"name": "owletId",
"displayName": "Owlet ID",
"type": "QString",
"defaultValue": ""
},
{
"id": "f6b60a4b-e7a2-4328-884d-818b0e2a361e",
"name": "pin",
"displayName": "Pin number",
"type": "uint",
"defaultValue": 1
}
],
"stateTypes": [
{
"id": "df1fbd9f-10b1-4788-a00e-de3f3f411cc6",
"name": "power",
"displayName": "Powered",
"type": "bool",
"defaultValue": false,
"displayNameEvent": "Powered changed",
"ioType": "digitalInput"
}
]
},
{
"id": "76f4ef8e-8e17-4528-a667-3d3f5afdd6a7",
"name": "ws2812",
"displayName": "WS2812 on owlet",
"createMethods": ["discovery"],
"interfaces": ["colorlight", "wirelessconnectable"],
"paramTypes": [
{
"id": "8c00f42b-5d34-4595-8ae9-6f48056a8be0",
"name": "owletId",
"displayName": "Owlet ID",
"type": "QString",
"defaultValue": ""
},
{
"id": "d674ee68-7f24-4dec-a75a-647a083d3580",
"name": "pin",
"displayName": "Pin number",
"type": "uint",
"defaultValue": 1
},
{
"id": "6c6df8eb-cdf1-424c-b29d-60f7dd19ae41",
"name": "ledCount",
"displayName": "LED count",
"type": "uint",
"defaultValue": 1
},
{
"id": "69c7f0e5-fdc4-4f9f-a117-90f165af3178",
"name": "ledMode",
"displayName": "LED color mode",
"type": "QString",
"allowedValues": [
"RGB",
"RBG",
"GRB",
"GBR",
"BRG",
"BGR",
"WRGB",
"WRBG",
"WGRB",
"WGBR",
"WBRG",
"WBRG",
"RWGB",
"RWBG",
"RGWB",
"RGBW",
"RBWG",
"RBGW",
"GWRB",
"GWBR",
"GRWB",
"GRBW",
"GBWR",
"GBRW",
"BWRG",
"BWGR",
"BRWG",
"BRGW",
"BGWR",
"BGRW"
],
"defaultValue": "RGB"
},
{
"id": "c4d99f98-1b46-4b38-bdd4-4bd5559dbb6f",
"name": "ledClock",
"displayName": "LED clock speed",
"type": "QString",
"allowedValues": ["400kHz", "800kHz"],
"defaultValue": "800kHz"
}
],
"stateTypes": [
{
"id": "0dbdd49b-578d-4404-87d2-b5a921df6aa6",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected or disconnected",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "58a8b3ca-720c-458e-b045-b99b5aadabd7",
"name": "power",
"displayName": "Power",
"displayNameEvent": "Power changed",
"displayNameAction": "Set power",
"type": "bool",
"defaultValue": false,
"writable": true
},
{
"id": "b56a9368-db2a-4ee6-99de-9ee8e1ffebd3",
"name": "brightness",
"displayName": "Brightness",
"displayNameEvent": "Brightness changed",
"displayNameAction": "Set brightness",
"type": "int",
"minValue": 0,
"maxValue": 100,
"unit": "Percentage",
"defaultValue": 100,
"writable": true
},
{
"id": "684c9118-20f3-41a0-928e-b7290d40166d",
"name": "color",
"displayName": "Color",
"displayNameEvent": "Color changed",
"displayNameAction": "Set color",
"type": "QColor",
"defaultValue": "white",
"writable": true
},
{
"id": "f92ea731-a86e-49b5-955b-9c245c7f874f",
"name": "colorTemperature",
"displayName": "Color temperature",
"displayNameEvent": "Color temperature changed",
"displayNameAction": "Set color temperature",
"type": "int",
"minValue": 0,
"maxValue": 100,
"defaultValue": 50,
"writable": true
}
]
}
]
}
]
}

13
owlet/owlet.pro Normal file
View File

@ -0,0 +1,13 @@
include(../plugins.pri)
QT += network
SOURCES += \
integrationpluginowlet.cpp \
owletclient.cpp
HEADERS += \
integrationpluginowlet.h \
owletclient.h