Skip to content

Commit

Permalink
Merge PR #494: WIP: New Plugin: nymea owlets
Browse files Browse the repository at this point in the history
  • Loading branch information
nymea-jenkins committed Nov 1, 2021
2 parents 385c317 + 841a0ed commit 5e6eef4
Show file tree
Hide file tree
Showing 9 changed files with 691 additions and 0 deletions.
11 changes: 11 additions & 0 deletions debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,17 @@ Description: nymea.io plugin for osdomotics
This package will install the nymea.io plugin for osdomotics


Package: nymea-plugin-owlet
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
nymea-plugins-translations,
Description: nymea.io plugin for nymea:owlet
nymea:owlet is a firmware for microcontrollers (Arduino/ESP8266/ESP32) which
exposes pins of the microcontroller to nymea and allows using them for
whatever purpose like moodlights, control relays, inputs etc.


Package: nymea-plugin-philipshue
Architecture: any
Depends: ${shlibs:Depends},
Expand Down
1 change: 1 addition & 0 deletions debian/nymea-plugin-owlet.install.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginowlet.so
1 change: 1 addition & 0 deletions nymea-plugins.pro
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ PLUGIN_DIRS = \
openuv \
openweathermap \
osdomotics \
owlet \
philipshue \
pushbullet \
pushnotifications \
Expand Down
248 changes: 248 additions & 0 deletions owlet/integrationpluginowlet.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, nymea GmbH
* Contact: [email protected]
*
* 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
* [email protected] 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>());
}
if (params.contains("effect")) {
thing->setStateValue(ws2812EffectStateTypeId, params.value("effect").toInt());
}
}
}
});

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));
}
if (info->action().actionTypeId() == ws2812EffectActionTypeId) {
int effect = info->action().paramValue(ws2812EffectActionEffectParamTypeId).toInt();
params.insert("effect", effect);
}

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)
}
63 changes: 63 additions & 0 deletions owlet/integrationpluginowlet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, nymea GmbH
* Contact: [email protected]
*
* 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
* [email protected] 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
Loading

0 comments on commit 5e6eef4

Please sign in to comment.