Skip to content

Commit

Permalink
Improved colorloop and scene handling
Browse files Browse the repository at this point in the history
- if a switch button is pressed a active colorloop will be stopped
- if a switch calls a scene the webview will be updated accordingly, further active colorloops will be stopped
  • Loading branch information
ChrisHae committed Apr 7, 2016
1 parent 01f0071 commit 736ec08
Show file tree
Hide file tree
Showing 8 changed files with 440 additions and 37 deletions.
272 changes: 268 additions & 4 deletions de_web_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ void DeRestPluginPrivate::apsdeDataIndication(const deCONZ::ApsDataIndication &i
case COMMISSIONING_CLUSTER_ID:
handleCommissioningClusterIndication(task, ind, zclFrame);
break;
case ONOFF_CLUSTER_ID:
handleOnOffClusterIndication(task, ind, zclFrame);
break;

default:
{
Expand Down Expand Up @@ -1400,6 +1403,16 @@ LightNode *DeRestPluginPrivate::updateLightNode(const deCONZ::NodeEvent &event)
updated = true;
}
}
else if (ia->id() == 0x4004) // color loop time
{
uint8_t clTime = ia->numericValue().u8;

if (lightNode->colorLoopSpeed() != clTime)
{
lightNode->setColorLoopSpeed(clTime);
updated = true;
}
}
}
}
else if (ic->id() == LEVEL_CLUSTER_ID && (event.clusterId() == LEVEL_CLUSTER_ID))
Expand Down Expand Up @@ -2233,6 +2246,35 @@ Sensor *DeRestPluginPrivate::getSensorNodeForAddress(quint64 extAddr)

}

/*! Returns the first Sensor for its given \p extAddress and \p Endpoint or 0 if not found.
*/
Sensor *DeRestPluginPrivate::getSensorNodeForAddressAndEndpoint(quint64 extAddr, quint8 ep)
{
std::vector<Sensor>::iterator i = sensors.begin();
std::vector<Sensor>::iterator end = sensors.end();

for (; i != end; ++i)
{
if (i->address().ext() == extAddr && ep == i->fingerPrint().endpoint && i->deletedState() != Sensor::StateDeleted)
{
return &(*i);
}
}

end = sensors.end();

for (i = sensors.begin(); i != end; ++i)
{
if (i->address().ext() == extAddr && ep == i->fingerPrint().endpoint)
{
return &(*i);
}
}

return 0;

}

/*! Returns the first Sensor which matches a fingerprint.
\note There might be more sensors with the same fingerprint.
*/
Expand Down Expand Up @@ -4535,7 +4577,8 @@ void DeRestPluginPrivate::handleSceneClusterIndication(TaskItem &task, const deC
li->setBri((uint8_t)lightNode->level());
li->setX(lightNode->colorX());
li->setY(lightNode->colorY());

li->setColorloopActive(lightNode->isColorLoopActive());
li->setColorloopTime(lightNode->colorLoopSpeed());
foundLightstate = true;
break;
}
Expand All @@ -4549,7 +4592,8 @@ void DeRestPluginPrivate::handleSceneClusterIndication(TaskItem &task, const deC
state.setBri((uint8_t)lightNode->level());
state.setX(lightNode->colorX());
state.setY(lightNode->colorY());

state.setColorloopActive(lightNode->isColorLoopActive());
state.setColorloopTime(lightNode->colorLoopSpeed());
scene->addLight(state);

// only change capacity and count when creating a new scene
Expand Down Expand Up @@ -4747,11 +4791,231 @@ void DeRestPluginPrivate::handleSceneClusterIndication(TaskItem &task, const deC
}
}

DBG_Printf(DBG_INFO, "Validaded Scene (gid: %u, sid: %u) for Light %s\n", groupId, sceneId, qPrintable(lightNode->id()));
DBG_Printf(DBG_INFO, "On: %u, Bri: %u, X: %u, Y: %u, Transitiontime: %u\n",
DBG_Printf(DBG_INFO_L2, "Validaded Scene (gid: %u, sid: %u) for Light %s\n", groupId, sceneId, qPrintable(lightNode->id()));
DBG_Printf(DBG_INFO_L2, "On: %u, Bri: %u, X: %u, Y: %u, Transitiontime: %u\n",
light.on(), light.bri(), light.x(), light.y(), light.transitiontime());
}
}
else if (zclFrame.commandId() == 0x05) // Recall scene command
{
if (!ind.srcAddress().hasExt())
{
return;
}

// update Nodes and Groups state if Recall scene Command was send by a switch
Sensor *sensorNode = getSensorNodeForAddressAndEndpoint(ind.srcAddress().ext(), ind.srcEndpoint());

if (sensorNode != 0)
{
if (sensorNode->deletedState() != Sensor::StateDeleted)
{
DBG_Assert(zclFrame.payload().size() >= 3);

QDataStream stream(zclFrame.payload());
stream.setByteOrder(QDataStream::LittleEndian);

uint16_t groupId;
uint8_t sceneId;

//stream >> status;
stream >> groupId;
stream >> sceneId;

// check if scene exists
Scene scene;
TaskItem task2;
bool colorloopDeactivated = false;
Group *group = getGroupForId(groupId);

std::vector<Scene>::const_iterator i = group->scenes.begin();
std::vector<Scene>::const_iterator end = group->scenes.end();

for (; i != end; ++i)
{
if ((i->id == sceneId) && (i->state != Scene::StateDeleted))
{
scene = *i;

std::vector<LightState>::const_iterator ls = scene.lights().begin();
std::vector<LightState>::const_iterator lsend = scene.lights().end();

for (; ls != lsend; ++ls)
{
LightNode *light = getLightNodeForId(ls->lid());
if (light && light->isAvailable() && light->state() != LightNode::StateDeleted)
{
bool changed = false;
if (ls->colorloopActive() == false && light->isColorLoopActive() != ls->colorloopActive())
{
//stop colorloop if scene was saved without colorloop (Osram don't stop colorloop if another scene is called)
task2.lightNode = light;
task2.req.dstAddress() = task2.lightNode->address();
task2.req.setTxOptions(deCONZ::ApsTxAcknowledgedTransmission);
task2.req.setDstEndpoint(task2.lightNode->haEndpoint().endpoint());
task2.req.setSrcEndpoint(getSrcEndpoint(task2.lightNode, task2.req));
task2.req.setDstAddressMode(deCONZ::ApsExtAddress);

light->setColorLoopActive(false);
addTaskSetColorLoop(task2, false, 15);

changed = true;
colorloopDeactivated = true;
}
//turn on colorloop if scene was saved with colorloop (FLS don't save colorloop at device)
else if (ls->colorloopActive() == true && light->isColorLoopActive() != ls->colorloopActive())
{
task2.lightNode = light;
task2.req.dstAddress() = task2.lightNode->address();
task2.req.setTxOptions(deCONZ::ApsTxAcknowledgedTransmission);
task2.req.setDstEndpoint(task2.lightNode->haEndpoint().endpoint());
task2.req.setSrcEndpoint(getSrcEndpoint(task2.lightNode, task2.req));
task2.req.setDstAddressMode(deCONZ::ApsExtAddress);

light->setColorLoopActive(true);
light->setColorLoopSpeed(ls->colorloopTime());
addTaskSetColorLoop(task2, true, ls->colorloopTime());
changed = true;
}
if (ls->on() == true && light->isOn() == false)
{
light->setIsOn(true);
changed = true;
}
if (ls->on() == false && light->isOn() == true)
{
light->setIsOn(false);
changed = true;
}
if ((uint16_t)ls->bri() != light->level())
{
light->setLevel((uint16_t)ls->bri());
changed = true;
}
if (changed == true)
{
updateEtag(light->etag);
}
}
}

//recall scene again
if (colorloopDeactivated)
{
callScene(group, sceneId);
}
break;
}
}

// turning 'on' the group is also a assumtion but a very likely one
if (!group->isOn())
{
group->setIsOn(true);
updateEtag(group->etag);
}

updateEtag(gwConfigEtag);

processTasks();
}
}
}
}

/*! Handle packets related to the ZCL On/Off cluster.
\param task the task which belongs to this response
\param ind the APS level data indication containing the ZCL packet
\param zclFrame the actual ZCL frame which holds the scene cluster reponse
*/
void DeRestPluginPrivate::handleOnOffClusterIndication(TaskItem &task, const deCONZ::ApsDataIndication &ind, deCONZ::ZclFrame &zclFrame)
{
Q_UNUSED(task);

if (!ind.srcAddress().hasExt())
{
return;
}

// update Nodes and Groups state if On/Off Command was send by a switch
Sensor *sensorNode = getSensorNodeForAddressAndEndpoint(ind.srcAddress().ext(), ind.srcEndpoint());

if (sensorNode != 0)
{
if (sensorNode->deletedState() != Sensor::StateDeleted) {

std::vector<Group>::iterator i = groups.begin();
std::vector<Group>::iterator end = groups.end();

for (; i != end; ++i)
{
if (i->state() != Group::StateDeleted && i->state() != Group::StateDeleteFromDB)
{
if (i->m_deviceMemberships.end() != std::find(i->m_deviceMemberships.begin(),
i->m_deviceMemberships.end(),
sensorNode->id()))
{
//found
if (zclFrame.commandId() == 0x00 || zclFrame.commandId() == 0x40) // Off || Off with effect
{
i->setIsOn(false);
}
else if (zclFrame.commandId() == 0x01) // On
{
i->setIsOn(true);
if (i->isColorLoopActive() == true)
{
TaskItem task1;
task1.req.dstAddress().setGroup(i->address());
task1.req.setDstAddressMode(deCONZ::ApsGroupAddress);
task1.req.setDstEndpoint(0xFF); // broadcast endpoint
task1.req.setSrcEndpoint(getSrcEndpoint(0, task1.req));

addTaskSetColorLoop(task1, false, 15);
i->setColorLoopActive(false);
}
}
updateEtag(i->etag);

// check each light if colorloop needs to be disabled
std::vector<LightNode>::iterator l = nodes.begin();
std::vector<LightNode>::iterator lend = nodes.end();

for (; l != lend; ++l)
{
if (isLightNodeInGroup(&(*l),i->address()))
{
if (zclFrame.commandId() == 0x00 || zclFrame.commandId() == 0x40) // Off || Off with effect
{
l->setIsOn(false);
}
else if (zclFrame.commandId() == 0x01) // On
{
l->setIsOn(true);

if (l->isAvailable() && l->state() != LightNode::StateDeleted && l->isColorLoopActive() == true)
{
TaskItem task2;
task2.lightNode = &(*l);
task2.req.dstAddress() = task2.lightNode->address();
task2.req.setTxOptions(deCONZ::ApsTxAcknowledgedTransmission);
task2.req.setDstEndpoint(task2.lightNode->haEndpoint().endpoint());
task2.req.setSrcEndpoint(getSrcEndpoint(task2.lightNode, task2.req));
task2.req.setDstAddressMode(deCONZ::ApsExtAddress);

addTaskSetColorLoop(task2, false, 15);
l->setColorLoopActive(false);
}
}
updateEtag(l->etag);
}
}
}
}
}
updateEtag(gwConfigEtag);
}
}
}

/*! Handle packets related to the ZCL Commissioning cluster.
Expand Down
6 changes: 5 additions & 1 deletion de_web_plugin_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,8 @@ enum TaskType
TaskRemoveScene,
TaskRemoveAllScenes,
TaskAddToGroup,
TaskRemoveFromGroup
TaskRemoveFromGroup,
TaskViewGroup
};

struct TaskItem
Expand Down Expand Up @@ -692,6 +693,7 @@ public Q_SLOTS:
void checkSensorNodeReachable(Sensor *sensor);
void updateSensorNode(const deCONZ::NodeEvent &event);
void checkAllSensorsAvailable();
Sensor *getSensorNodeForAddressAndEndpoint(quint64 extAddr, quint8 ep);
Sensor *getSensorNodeForAddress(quint64 extAddr);
Sensor *getSensorNodeForFingerPrint(quint64 extAddr, const SensorFingerprint &fingerPrint, const QString &type);
Sensor *getSensorNodeForUniqueId(const QString &uniqueId);
Expand Down Expand Up @@ -749,13 +751,15 @@ public Q_SLOTS:
bool addTaskSetColorLoop(TaskItem &task, bool colorLoopActive, uint8_t speed);
bool addTaskIdentify(TaskItem &task, uint16_t identifyTime);
bool addTaskAddToGroup(TaskItem &task, uint16_t groupId);
bool addTaskViewGroup(TaskItem &task, uint16_t groupId);
bool addTaskRemoveFromGroup(TaskItem &task, uint16_t groupId);
bool addTaskStoreScene(TaskItem &task, uint16_t groupId, uint8_t sceneId);
bool addTaskAddScene(TaskItem &task, uint16_t groupId, uint8_t sceneId, QString lightId);
bool addTaskRemoveScene(TaskItem &task, uint16_t groupId, uint8_t sceneId);
bool obtainTaskCluster(TaskItem &task, const deCONZ::ApsDataIndication &ind);
void handleGroupClusterIndication(TaskItem &task, const deCONZ::ApsDataIndication &ind, deCONZ::ZclFrame &zclFrame);
void handleSceneClusterIndication(TaskItem &task, const deCONZ::ApsDataIndication &ind, deCONZ::ZclFrame &zclFrame);
void handleOnOffClusterIndication(TaskItem &task, const deCONZ::ApsDataIndication &ind, deCONZ::ZclFrame &zclFrame);
void handleCommissioningClusterIndication(TaskItem &task, const deCONZ::ApsDataIndication &ind, deCONZ::ZclFrame &zclFrame);
void handleDeviceAnnceIndication(const deCONZ::ApsDataIndication &ind);
void handleMgmtBindRspIndication(const deCONZ::ApsDataIndication &ind);
Expand Down
17 changes: 16 additions & 1 deletion light_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ LightNode::LightNode() :
m_colorTemperature(0),
m_colorMode("hs"),
m_colorLoopActive(false),
m_colorLoopSpeed(0),
m_groupCount(0),
m_sceneCapacity(0)
m_sceneCapacity(16)

{
}
Expand Down Expand Up @@ -422,6 +423,20 @@ bool LightNode::isColorLoopActive() const
return m_colorLoopActive;
}

/*! Sets the nodes color loop speed state.
\param colorLoopActive whereever the color loop is active
*/
void LightNode::setColorLoopSpeed(uint8_t speed)
{
m_colorLoopSpeed = speed;
}

/*! Returns the nodes color loop speed state. */
uint8_t LightNode::colorLoopSpeed() const
{
return m_colorLoopSpeed;
}

/*! Returns the lights HA endpoint descriptor.
*/
const deCONZ::SimpleDescriptor &LightNode::haEndpoint() const
Expand Down
3 changes: 3 additions & 0 deletions light_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ class LightNode : public RestNodeBase
void setColorMode(const QString &colorMode);
void setColorLoopActive(bool colorLoopActive);
bool isColorLoopActive() const;
void setColorLoopSpeed(uint8_t speed);
uint8_t colorLoopSpeed() const;
const deCONZ::SimpleDescriptor &haEndpoint() const;
void setHaEndpoint(const deCONZ::SimpleDescriptor &endpoint);
uint8_t groupCapacity() const;
Expand Down Expand Up @@ -107,6 +109,7 @@ class LightNode : public RestNodeBase
uint16_t m_colorTemperature;
QString m_colorMode;
bool m_colorLoopActive;
uint8_t m_colorLoopSpeed;
deCONZ::SimpleDescriptor m_haEndpoint;
uint8_t m_groupCount;
uint8_t m_sceneCapacity;
Expand Down
Loading

0 comments on commit 736ec08

Please sign in to comment.