Skip to content

Commit

Permalink
add plugin content and update LICENSE
Browse files Browse the repository at this point in the history
  • Loading branch information
manup committed Jul 23, 2013
1 parent b642325 commit d5bc09c
Show file tree
Hide file tree
Showing 38 changed files with 156,943 additions and 3 deletions.
2 changes: 1 addition & 1 deletion LICENSE → LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ are permitted provided that the following conditions are met:
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

Neither the name of the {organization} nor the names of its
Neither the name of dresden elektronik ingenieurtechnik gmbh nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

Expand Down
63 changes: 61 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,61 @@
deconz-rest-plugin
==================
Introduction
============

The [deCONZ](http://www.dresden-elektronik.de/funktechnik/products/software/pc/deconz?L=1) REST plugin provides a REST API to access ZigBee Home Automation (HA) and ZigBee Light Link (ZLL) lights like dresden elektroniks [Wireless Light Control](http://www.dresden-elektronik.de/funktechnik/solutions/wireless-light-control) system and Philips Hue.

As hardware the [RaspBee](http://www.dresden-elektronik.de/funktechnik/solutions/wireless-light-control/raspbee?L=1) ZigBee Shield for Raspberry Pi is used to directly communicate with the ZigBee devices.

To learn more about the REST API itself please visit the [REST API Documentation](http://dresden-elektronik.github.io/deconz-rest-doc/) page.

License
=======
The plugin is available as open source and licensed under the BSD (3-Clause) license.


Usage
=====

Currently the compilation of the plugin is only supported within the Raspbian distribution.

##### Install deCONZ and development package
1. Download deCONZ package

`wget http://www.dresden-elektronik.de/rpi/deconz/deconz-latest.deb`

2. Install deCONZ package

`sudo dpkg -i deconz-latest.deb`

3. Download deCONZ development package

`wget http://www.dresden-elektronik.de/rpi/deconz-dev/deconz-dev-latest.deb`

4. Install deCONZ development package

`sudo dpkg -i deconz-dev-latest.deb`

##### Get and compile the plugin
1. Checkout the repository

`git clone https://github.com/dresden-elektronik/deconz-rest-plugin.git`

2. Compile the plugin

`cd deconz-rest-plugin`

`qmake-qt4 && make`

Hardware requirements
---------------------

* Raspberry Pi
* [RaspBee](http://www.dresden-elektronik.de/funktechnik/solutions/wireless-light-control/raspbee?L=1) ZigBee Shield for Raspberry Pi
* or a deRFusb23e0x wireless USB dongle

3rd party libraries
-------------------
The following libraries are used by the plugin:

* [SQLite](http://www.sqlite.org)
* [qt-json](https://github.com/lawand/droper/tree/master/qt-json)
* [colorspace](http://www.getreuer.info/home/colorspace)
150 changes: 150 additions & 0 deletions authentification.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* Copyright (C) 2013 dresden elektronik ingenieurtechnik gmbh.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*/

#include "de_web_plugin_private.h"
#include <QHttpResponseHeader>
#ifdef Q_OS_UNIX
#include <unistd.h>
#endif

#ifdef Q_OS_UNIX
static const char *pwsalt = "$1$8282jdkmskwiu29291"; // $1$ for MD5
#endif

ApiAuth::ApiAuth()
{

}

/*! Init authentification and security.
*/
void DeRestPluginPrivate::initAuthentification()
{
bool ok = false;

if (gwConfig.contains("gwusername") && gwConfig.contains("gwpassword"))
{
gwAdminUserName = gwConfig["gwusername"].toString();
gwAdminPasswordHash = gwConfig["gwpassword"].toString();

if (!gwAdminUserName.isEmpty() && !gwAdminPasswordHash.isEmpty())
{
ok = true;
}
}

if (!ok)
{
// generate default username and password
gwAdminUserName = "delight";
gwAdminPasswordHash = "delight";

DBG_Printf(DBG_INFO, "create default username and password\n");

// combine username:password
QString comb = QString("%1:%2").arg(gwAdminUserName).arg(gwAdminPasswordHash);
// create base64 encoded version as used in HTTP basic authentification
QString hash = comb.toLocal8Bit().toBase64();

gwAdminPasswordHash = encryptString(hash);

needSaveDatabase = true;
}

}

/*! Use HTTP basic authentification to check if the request
has valid credentials to create API key.
*/
bool DeRestPluginPrivate::allowedToCreateApikey(const ApiRequest &req)
{
if (req.hdr.hasKey("Authorization"))
{
QStringList ls = req.hdr.value("Authorization").split(' ');
if ((ls.size() > 1) && ls[0] == "Basic")
{
QString enc = encryptString(ls[1]);

if (enc == gwAdminPasswordHash)
{
return true;
}

DBG_Printf(DBG_INFO, "Invalid admin password hash: %s\n", qPrintable(enc));
}
}

return false;
}

/*! Checks if the request is authenticated to access the API.
\retval true if authenticated
\retval false if not authenticated and the rsp http status is set to 403 Forbidden and JSON error is appended
*/
bool DeRestPluginPrivate::checkApikeyAuthentification(const ApiRequest &req, ApiResponse &rsp)
{
QString apikey = req.apikey();

if (apikey.isEmpty())
{
return false;
}

std::vector<ApiAuth>::const_iterator i = apiAuths.begin();
std::vector<ApiAuth>::const_iterator end = apiAuths.end();

for (; i != end; ++i)
{
if (apikey == i->apikey)
{
return true;
}
}

// allow non registered devices to use the api if the link button is pressed
if (gwLinkButton)
{
ApiAuth auth;
auth.apikey = apikey;
auth.devicetype = "unknown";
apiAuths.push_back(auth);
needSaveDatabase = true;
return true;
}

rsp.httpStatus = HttpStatusForbidden;
rsp.list.append(errorToMap(ERR_UNAUTHORIZED_USER, req.path.join("/"), "unauthorized user"));

return false;
}

/*! Encrypts a string with using crypt() MD5 + salt. (unix only)
\param str the input string
\return the encrypted string on success or the unchanged input string on fail
*/
QString DeRestPluginPrivate::encryptString(const QString &str)
{
#ifdef Q_OS_UNIX
// further encrypt and salt the hash
const char *enc = crypt(str.toLocal8Bit().constData(), pwsalt);

if (enc)
{
return QString(enc);
}
else
{
DBG_Printf(DBG_ERROR, "crypt(): %s failed\n", qPrintable(str));
// fall through and return str
}

#endif // Q_OS_UNIX
return str;
}
88 changes: 88 additions & 0 deletions change_channel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright (C) 2013 dresden elektronik ingenieurtechnik gmbh.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*/

#include "de_web_plugin_private.h"

void DeRestPluginPrivate::changeChannel(int channel)
{
DBG_Assert(channel >= 11 && channel <= 26);

if ((channel >= 11) && (channel <= 26))
{


deCONZ::ApsDataRequest req;

req.setTxOptions(0);
req.setDstEndpoint(0);
// req.setDstAddressMode(deCONZ::ApsGroupAddress);
// req.dstAddress().setGroup(groupId);
req.setDstAddressMode(deCONZ::ApsNwkAddress);
req.dstAddress().setNwk(0xFFFF);
req.setProfileId(0x0000); // ZDP profile
req.setClusterId(0x0038); // Mgmt_NWK_Update_req
req.setSrcEndpoint(0);
req.setRadius(0);

uint8_t zdpSeq = 0x77;
uint32_t scanChannels = (1 << (uint)channel);
uint8_t scanDuration = 0xfe; //special value = channel change
uint8_t nwkUpdateId = 0x00;

QDataStream stream(&req.asdu(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << zdpSeq;
stream << scanChannels;
stream << scanDuration;
stream << nwkUpdateId;

deCONZ::ApsController *apsCtrl = deCONZ::ApsController::instance();

if (apsCtrl->apsdeDataRequest(req) == 0)
{
DBG_Printf(DBG_INFO, "change channel to %d, channel mask = 0x%08lX\n", channel, scanChannels);
}
else
{
DBG_Printf(DBG_ERROR, "cant send change channel\n");
}
#if 0
req.setTxOptions(0);
req.setDstEndpoint(0);
// req.setDstAddressMode(deCONZ::ApsGroupAddress);
// req.dstAddress().setGroup(groupId);
req.setDstAddressMode(deCONZ::ApsExtAddress);
req.dstAddress().setExt(0x17880100b829e1); // TODO: use selected node
req.setProfileId(0x0000); // ZDP profile
req.setClusterId(0x0034); // Mgmt_Leave_req
req.setSrcEndpoint(0);
req.setRadius(0);

uint8_t zdpSeq = 0x77;
uint64_t deviceAddress = 0;
uint8_t flags = 0x00;

QDataStream stream(&req.asdu(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << zdpSeq;
stream << deviceAddress;
stream << flags;

if (apsCtrl->apsdeDataRequest(req) == 0)
{
DBG_Printf(DBG_INFO, "send mgmt leave\n");
}
else
{
DBG_Printf(DBG_ERROR, "cant send mgmt leave\n");
}
#endif
}
}
Loading

0 comments on commit d5bc09c

Please sign in to comment.