Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gateway cascade enhancements (and some more) #338

Merged
merged 9 commits into from
Jan 4, 2018
8 changes: 6 additions & 2 deletions de_web_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2694,13 +2694,17 @@ void DeRestPluginPrivate::addSensorNode(const deCONZ::Node *node)

case ANALOG_INPUT_CLUSTER_ID:
{
fpSwitch.inClusters.push_back(ci->id());
if (modelId == QLatin1String("lumi.sensor_cube")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may need to extend this whitelist in future, I'm not sure but I think there were other sensors using the analog/multi-state clusters too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I double-checked that I introduced these clusters when adding support for the Smart Cube. Also, the handling of the attribute values is specific to the cube.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively we could to blacklist the “funny” sensors (so far I’ve seen reports of lumi.sensor_ht and lumi.switch_86sw2 showing additional endpoints with these clusters).

fpSwitch.inClusters.push_back(ci->id());
}
}
break;

case MULTISTATE_INPUT_CLUSTER_ID:
{
fpSwitch.inClusters.push_back(ci->id());
if (modelId == QLatin1String("lumi.sensor_cube")) {
fpSwitch.inClusters.push_back(ci->id());
}
}
break;

Expand Down
13 changes: 13 additions & 0 deletions de_web_plugin_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,20 @@
#define ONOFF_COMMAND_OFF 0x00
#define ONOFF_COMMAND_ON 0x01
#define ONOFF_COMMAND_TOGGLE 0x02
#define ONOFF_COMMAND_OFF_WITH_EFFECT 0x040
#define ONOFF_COMMAND_ON_WITH_TIMED_OFF 0x42
#define LEVEL_COMMAND_MOVE_TO_LEVEL 0x00
#define LEVEL_COMMAND_MOVE 0x01
#define LEVEL_COMMAND_STEP 0x02
#define LEVEL_COMMAND_STOP 0x03
#define LEVEL_COMMAND_MOVE_TO_LEVEL_WITH_ON_OFF 0x04
#define LEVEL_COMMAND_MOVE_WITH_ON_OFF 0x05
#define LEVEL_COMMAND_STEP_WITH_ON_OFF 0x06
#define LEVEL_COMMAND_STOP_WITH_ON_OFF 0x07
#define SCENE_COMMAND_RECALL_SCENE 0x05
#define SCENE_COMMAND_IKEA_STEP_CT 0x07
#define SCENE_COMMAND_IKEA_MOVE_CT 0x08
#define SCENE_COMMAND_IKEA_STOP_CT 0x09

// read flags
#define READ_MODEL_ID (1 << 0)
Expand Down
260 changes: 234 additions & 26 deletions gateway.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@
#include <QTime>
#include <deconz.h>
#include "gateway.h"
#include "group.h"
#include "json.h"


#define ONOFF_COMMAND_OFF 0x00
#define ONOFF_COMMAND_ON 0x01
#define ONOFF_COMMAND_TOGGLE 0x02
#define ONOFF_COMMAND_ON_WITH_TIMED_OFF 0x42
#define PHILIPS_MAC_PREFIX QLatin1String("001788")

enum GW_Event
{
Expand All @@ -32,6 +30,7 @@ class Command
quint8 sceneId;
quint8 level;
} param;
quint8 mode;
quint16 transitionTime;
};

Expand All @@ -46,6 +45,7 @@ class GatewayPrivate
void checkConfigResponse(const QByteArray &data);
void checkGroupsResponse(const QByteArray &data);

DeRestPluginPrivate *parent;
Gateway::State state;
bool pairingEnabled;
bool needSaveDatabase;
Expand All @@ -65,11 +65,12 @@ class GatewayPrivate
std::vector<Command> commands;
};

Gateway::Gateway(QObject *parent) :
Gateway::Gateway(DeRestPluginPrivate *parent) :
QObject(parent),
d_ptr(new GatewayPrivate)
{
Q_D(Gateway);
d->parent = parent;
d->pings = 0;
d->state = Gateway::StateOffline;
d->pairingEnabled = false;
Expand Down Expand Up @@ -265,21 +266,118 @@ void Gateway::handleGroupCommand(const deCONZ::ApsDataIndication &ind, deCONZ::Z
{
Command cmd;

cmd.transitionTime = 0;

// filter
if (ind.clusterId() == 0x0005 && zclFrame.commandId() == 0x05) // recall scene
if (ind.clusterId() == SCENE_CLUSTER_ID)
{
if (zclFrame.payload().size() < 3) // sanity
continue;

// payload U16 group, U8 scene
cmd.param.sceneId = zclFrame.payload().at(2);
switch(zclFrame.commandId())
{
case SCENE_COMMAND_RECALL_SCENE:
if (zclFrame.payload().size() < 3) // sanity
{
continue;
}
// payload U16 group, U8 scene
cmd.param.sceneId = zclFrame.payload().at(2);
break;
case SCENE_COMMAND_IKEA_MOVE_CT:
cmd.mode = zclFrame.payload().at(0);
cmd.transitionTime = 2540.0 / 83; // value for DimUp/Down
break;
case SCENE_COMMAND_IKEA_STEP_CT:
// payload U8 mode
cmd.mode = zclFrame.payload().at(0);
cmd.param.level = 43; // value for DimUp/Down
cmd.transitionTime = 5; // value for DimUp/Down
break;
case SCENE_COMMAND_IKEA_STOP_CT:
break;
default:
continue;
}
}
else if (ind.clusterId() == 0x0006) // onoff
else if (ind.clusterId() == ONOFF_CLUSTER_ID) // onoff
{
switch(zclFrame.commandId())
{
case ONOFF_COMMAND_OFF:
// Set on: false trough REST API
break;
case ONOFF_COMMAND_ON:
// Set on: true trough REST API
// Hue dimmer switch On
break;
case ONOFF_COMMAND_TOGGLE:
// IKEA Trådfri Remote On/Off
{
::Group *group;
QVariantMap map;
QVariantMap state;

group = d->parent->getGroupForId(cg.local);
if (group && d->parent->groupToMap(group , map))
{
state = map["state"].toMap();
cmd.param.level = state["all_on"].toBool() ? 0x00 : 0x01;
break;
}
}
continue;
case ONOFF_COMMAND_OFF_WITH_EFFECT:
// Hue dimmer switch Off
cmd.transitionTime = 4;
break;
case ONOFF_COMMAND_ON_WITH_TIMED_OFF:
// IKEA Trådfri motion sensor
break;
default:
continue;
}
}
else if (ind.clusterId() == LEVEL_CLUSTER_ID)
{
switch (zclFrame.commandId())
{
case LEVEL_COMMAND_MOVE_TO_LEVEL:
// Set bri through REST API
// payload U8 level, U16 transition time
cmd.param.level = zclFrame.payload().at(0);
cmd.transitionTime = zclFrame.payload().at(1);
break;
case LEVEL_COMMAND_MOVE_WITH_ON_OFF:
// IKEA Trådfri remote DimUp Hold
// fall through
case LEVEL_COMMAND_MOVE:
// IKEA Trådfri remote DimDown Hold
// payload U8 mode, U8 rate
cmd.mode = zclFrame.payload().at(0);
cmd.param.level = zclFrame.payload().at(1);
// DBG_Printf(DBG_INFO_L2, "GW level %u\n", cmd.param.level);
cmd.transitionTime = 2540.0 / cmd.param.level;
break;
case LEVEL_COMMAND_STEP_WITH_ON_OFF:
// IKEA Trådfri remote DimUp Short Release
// fall through
case LEVEL_COMMAND_STEP:
// Hue dimmer switch DimUp, DimDown Press, Hold
// IKEA Trådfri remote DimDown Short Release
// payload U8 mode, U8 step size, U16 transitionTime
cmd.mode = zclFrame.payload().at(0);
cmd.param.level = zclFrame.payload().at(1);
cmd.transitionTime = zclFrame.payload().at(2);
break;
case LEVEL_COMMAND_STOP_WITH_ON_OFF:
// IKEA Trådfri remote DimUp Long Release
// fall through
case LEVEL_COMMAND_STOP:
// Hue dimmer switch DimUp, DimDown Long Release
// IKEA Trådfri remote DimDown Long Release
break;
default:
continue;
}
}
// else if (ind.clusterId() == 0x0008) // level
// {
// }
else
{
continue;
Expand All @@ -288,7 +386,6 @@ void Gateway::handleGroupCommand(const deCONZ::ApsDataIndication &ind, deCONZ::Z
cmd.clusterId = ind.clusterId();
cmd.groupId = cg.remote;
cmd.commandId = zclFrame.commandId();
cmd.transitionTime = 0;
d->commands.push_back(cmd);
d->handleEvent(EventCommandAdded);

Expand Down Expand Up @@ -526,36 +623,147 @@ void GatewayPrivate::handleEventStateConnected(GW_Event event)
}
else
{
bool ok = false;
double level;
QString url;
QVariantMap map;
const Command &cmd = commands.back();

if (cmd.clusterId == 0x0005 && cmd.commandId == 0x05) // recall scene
if (cmd.clusterId == SCENE_CLUSTER_ID)
{
url.sprintf("http://%s:%u/api/%s/groups/%u/scenes/%u/recall",
qPrintable(address.toString()), port, qPrintable(apikey), cmd.groupId, cmd.param.sceneId);
switch (cmd.commandId) {
case SCENE_COMMAND_RECALL_SCENE:
ok = true;
if (uuid.startsWith(PHILIPS_MAC_PREFIX)) // cascade gateway is Hue bridge
{
QString scene;
scene.sprintf("g%us%u", cmd.groupId, cmd.param.sceneId);
map[QLatin1String("scene")] = scene;
}
else
{
url.sprintf("http://%s:%u/api/%s/groups/%u/scenes/%u/recall",
qPrintable(address.toString()), port, qPrintable(apikey), cmd.groupId, cmd.param.sceneId);
}
break;
case SCENE_COMMAND_IKEA_STEP_CT:
ok = true;
level = cmd.param.level * (cmd.mode == 0x00 ? 1 : -1);
map[QLatin1String("ct_inc")] = level;
break;
case SCENE_COMMAND_IKEA_MOVE_CT:
ok = true;
level = cmd.mode == 0x00 ? 254 : -254;
map[QLatin1String("ct_inc")] = level;
break;
case SCENE_COMMAND_IKEA_STOP_CT:
ok = true;
level = 0;
map[QLatin1String("ct_inc")] = level;
break;
default:
break;
}
}
else if (cmd.clusterId == 0x0006)
else if (cmd.clusterId == ONOFF_CLUSTER_ID) // onoff
{
url.sprintf("http://%s:%u/api/%s/groups/%u/action",
qPrintable(address.toString()), port, qPrintable(apikey), cmd.groupId);

if (cmd.commandId == ONOFF_COMMAND_ON) { map[QLatin1String("on")] = true; }
if (cmd.commandId == ONOFF_COMMAND_OFF) { map[QLatin1String("on")] = false; }
switch (cmd.commandId) {
case ONOFF_COMMAND_OFF_WITH_EFFECT:
// Hue dimmer switch Off
// fall through
case ONOFF_COMMAND_OFF:
// Set on: false through REST API
ok = true;
map[QLatin1String("on")] = false;
break;
case ONOFF_COMMAND_ON:
// Set on: true through REST API
// Hue dimmer switch On
ok = true;
map[QLatin1String("on")] = true;
break;
case ONOFF_COMMAND_TOGGLE:
ok = true;
map[QLatin1String("on")] = cmd.param.level == 0x01;
break;
default:
break;
}
}
else if (cmd.clusterId == LEVEL_CLUSTER_ID)
{
switch (cmd.commandId)
{
case LEVEL_COMMAND_MOVE_TO_LEVEL:
// Set bri through REST API
ok = true;
level = cmd.param.level;
map[QLatin1String("bri")] = level;
break;
case LEVEL_COMMAND_MOVE_WITH_ON_OFF:
if (cmd.mode == 0x00)
{
map[QLatin1String("on")] = true;
}
// fall through
case LEVEL_COMMAND_MOVE:
// IKEA Trådfri remote DimDown Hold
ok = true;
level = cmd.mode == 0x00 ? 254 : -254;
map[QLatin1String("bri_inc")] = level;
break;
case LEVEL_COMMAND_STEP_WITH_ON_OFF:
// IKEA Trådfri remote DimUp Short Release
if (cmd.mode == 0x00)
{
map[QLatin1String("on")] = true;
}
// fall through
case LEVEL_COMMAND_STEP:
// Hue dimmer switch DimUp, DimDown Short Release, Hold
// IKEA Trådfri remote DimDown Short Release
ok = true;
level = cmd.param.level * (cmd.mode == 0x00 ? 1 : -1);
map[QLatin1String("bri_inc")] = level;
break;
case LEVEL_COMMAND_STOP_WITH_ON_OFF:
// IKEA Trådfri remote DimUp Long Release
// fall through
case LEVEL_COMMAND_STOP:
// Philips Hue dimmer DimUp, DimDown Long Release
// IKEA Trådfri remote DimDown Long Release
ok = true;
level = 0;
map[QLatin1String("bri_inc")] = level;
break;
default:
break;
}
}

commands.pop_back();

if (url.isEmpty())
if (!ok)
{
startTimer(50, EventTimeout);
return;
}

if (url.isEmpty())
{
url.sprintf("http://%s:%u/api/%s/groups/%u/action",
qPrintable(address.toString()), port, qPrintable(apikey), cmd.groupId);
}

QString json;
if (!map.isEmpty())
{
if (cmd.transitionTime != 0)
{
map[QLatin1String("transitiontime")] = (double) cmd.transitionTime;
}
json = deCONZ::jsonStringFromMap(map);
DBG_Printf(DBG_INFO_L2, "GW body %s\n", qPrintable(json));
}
else
{
Expand Down
4 changes: 3 additions & 1 deletion gateway.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <QObject>
#include <QHostAddress>
#include <QNetworkReply>
#include "de_web_plugin.h"
#include "de_web_plugin_private.h"

namespace deCONZ {
class ApsDataIndication;
Expand Down Expand Up @@ -37,7 +39,7 @@ class Gateway : public QObject
StateConnected
};

explicit Gateway(QObject *parent = 0);
explicit Gateway(DeRestPluginPrivate *parent = 0);
~Gateway();

const QString &name() const;
Expand Down
Loading