diff --git a/philipshue/hueremote.cpp b/philipshue/hueremote.cpp
index 8e171bba..0e9cec44 100644
--- a/philipshue/hueremote.cpp
+++ b/philipshue/hueremote.cpp
@@ -61,24 +61,20 @@ void HueRemote::updateStates(const QVariantMap &statesMap, const QVariantMap &co
QString lastUpdate = statesMap.value("lastupdated").toString();
int buttonCode = statesMap.value("buttonevent").toInt();
- int rotationCode = statesMap.value("expectedrotation").toInt();
// If we never polled, just store lastUpdate/buttonCode/rotationCode and not emit a falsely button pressed event
if (m_lastUpdate.isEmpty() || m_lastButtonCode == -1) {
m_lastUpdate = lastUpdate;
m_lastButtonCode = buttonCode;
- m_lastRotationCode = rotationCode;
}
- if (m_lastUpdate != lastUpdate || m_lastButtonCode != buttonCode || m_lastRotationCode != rotationCode) {
+ if (m_lastUpdate != lastUpdate || m_lastButtonCode != buttonCode) {
m_lastUpdate = lastUpdate;
m_lastButtonCode = buttonCode;
- m_lastRotationCode = rotationCode;
qCDebug(dcPhilipsHue) << "button pressed" << buttonCode;
emit buttonPressed(buttonCode);
- emit rotated(rotationCode);
}
}
diff --git a/philipshue/hueremote.h b/philipshue/hueremote.h
index 9e5ea769..9f3d1253 100644
--- a/philipshue/hueremote.h
+++ b/philipshue/hueremote.h
@@ -55,12 +55,10 @@ private:
int m_battery;
QString m_lastUpdate;
int m_lastButtonCode = -1;
- int m_lastRotationCode = 0;
signals:
void stateChanged();
void buttonPressed(int buttonCode);
- void rotated(int rotationCode);
};
diff --git a/philipshue/huetapdial.cpp b/philipshue/huetapdial.cpp
new file mode 100644
index 00000000..31d64e56
--- /dev/null
+++ b/philipshue/huetapdial.cpp
@@ -0,0 +1,163 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, 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 .
+*
+* 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 "huetapdial.h"
+#include "extern-plugininfo.h"
+
+#include
+
+HueTapDial::HueTapDial(HueBridge *bridge, QObject *parent) :
+ HueDevice(bridge, parent)
+{
+}
+
+int HueTapDial::rotaryId() const
+{
+ return m_rotaryId;
+}
+
+void HueTapDial::setRotaryId(int sensorId)
+{
+ m_rotaryId = sensorId;
+}
+
+QString HueTapDial::rotaryUuid() const
+{
+ return m_rotaryUuid;
+}
+
+void HueTapDial::setRotaryUuid(const QString &rotaryUuid)
+{
+ m_rotaryUuid = rotaryUuid;
+}
+
+int HueTapDial::switchId() const
+{
+ return m_switchId;
+}
+
+void HueTapDial::setSwitchId(int sensorId)
+{
+ m_SwitchId = sensorId;
+}
+
+QString HueTapDial::switchUuid() const
+{
+ return m_switchUuid;
+}
+
+void HueTapDial::setSwitchUuid(const QString &switchUuid)
+{
+ m_switchUuid = switchUuid;
+}
+
+double HueTapDial::level() const
+{
+ return m_level;
+}
+
+int HueTapDial::batteryLevel() const
+{
+ return m_batteryLevel;
+}
+
+void HueTapDial::updateStates(const QVariantMap &sensorMap)
+{
+ qCDebug(dcPhilipsHue()) << "Hue Tap Dial data:" << qUtf8Printable(QJsonDocument::fromVariant(sensorMap).toJson(QJsonDocument::Indented));
+
+ // Config
+ QVariantMap configMap = sensorMap.value("config").toMap();
+ if (configMap.contains("reachable")) {
+ setReachable(configMap.value("reachable", false).toBool());
+ // emit reachableChanged????
+ }
+
+ if (configMap.contains("battery")) {
+ int batteryLevel = configMap.value("battery", 0).toInt();
+ if (m_batteryLevel != batteryLevel) {
+ m_batteryLevel = batteryLevel;
+ emit batteryLevelChanged(m_batteryLevel);
+ }
+ }
+
+ // States
+ QVariantMap stateMap = sensorMap.value("state").toMap();
+
+ // If rotated
+ if (sensorMap.value("uniqueid").toString() == m_rotaryUuid) {
+ QString lastUpdate = stateMap.value("lastupdated").toString();
+ int rotationCode = stateMap.value("expectedrotation").toInt();
+
+ // If we never polled, just store lastUpdate/rotationCode and not emit a false rotated event
+ if (m_lastUpdate.isEmpty() || m_lastRotationCode == 0) {
+ m_lastUpdate = lastUpdate;
+ m_lastRotationCode = rotationCode;
+ }
+ if (m_lastUpdate != lastUpdate || m_lastRotationCode != rotationCode) {
+ m_lastUpdate = lastUpdate;
+ m_lastRotationCode = rotationCode;
+ qCDebug(dcPhilipsHue) << "rotated" << rotationCode;
+ emit rotated(rotationCode);
+ }
+ }
+
+ // If button press
+ if (sensorMap.value("uniqueid").toString() == m_switchUuid) {
+ QString lastUpdate = stateMap.value("lastupdated").toString();
+ int buttonCode = stateMap.value("buttonevent").toInt();
+
+ // If we never polled, just store lastUpdate/buttonCode and not emit a false button pressed event
+ if (m_lastUpdate.isEmpty() || m_lastButtonCode == -1) {
+ m_lastUpdate = lastUpdate;
+ m_lastButtonCode = buttonCode;
+ }
+ if (m_lastUpdate != lastUpdate || m_lastButtonCode != buttonCode) {
+ m_lastUpdate = lastUpdate;
+ m_lastButtonCode = buttonCode;
+ qCDebug(dcPhilipsHue) << "button pressed" << buttonCode;
+ emit buttonPressed(buttonCode);
+ }
+ }
+}
+
+bool HueTapDial::isValid()
+{
+ return !m_rotaryUuid.isEmpty() && !m_switchUuid.isEmpty();
+}
+
+bool HueTapDial::hasSensor(int sensorId)
+{
+ return m_rotaryId == sensorId || m_switchId == sensorId;
+}
+
+bool HueTapDial::hasSensor(const QString &sensorUuid)
+{
+ return m_rotaryUuid == sensorUuid || m_switchUuid == sensorUuid;
+}
diff --git a/philipshue/huetapdial.h b/philipshue/huetapdial.h
new file mode 100644
index 00000000..546f251a
--- /dev/null
+++ b/philipshue/huetapdial.h
@@ -0,0 +1,101 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, 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 .
+*
+* 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 HUETAPDIAL_H
+#define HUETAPDIAL_H
+
+#include
+#include
+
+#include "extern-plugininfo.h"
+#include "huedevice.h"
+
+class HueTapDial : public HueDevice
+{
+ Q_OBJECT
+public:
+ explicit HueTapDial(HueBridge *bridge, QObject *parent = nullptr);
+ //virtual ~HueTapDial() = default;
+
+ int rotaryId() const;
+ void setRotaryId(int sensorId);
+
+ QString rotaryUuid() const;
+ void setRotaryUuid(const QString &rotaryUuid);
+
+ int switchId() const;
+ void setSwitchId(int sensorId);
+
+ QString switchUuid() const;
+ void setSwitchUuid(const QString &switchUuid);
+
+ int level() const;
+ int batteryLevel() const;
+
+ void updateStates(const QVariantMap &sensorMap);
+
+ bool isValid();
+ bool hasSensor(int sensorId);
+ bool hasSensor(const QString &sensorUuid);
+
+ // virtual StateTypeId connectedStateTypeId() const = 0;
+ // virtual StateTypeId levelStateTypeId() const = 0;
+ // virtual StateTypeId batteryLevelStateTypeId() const = 0;
+ // virtual StateTypeId batteryCriticalStateTypeId() const = 0;
+
+ // StateTypeId connectedStateTypeId() const override { return tapDialConnectedStateTypeId; }
+ // StateTypeId levelStateTypeId() const override { return tapDialLevelStateTypeId; }
+ // StateTypeId batteryLevelStateTypeId() const override { return tapDialBatteryLevelStateTypeId; }
+ // StateTypeId batteryCriticalStateTypeId() const override { return tapDialBatteryCriticalStateTypeId; }
+
+private:
+ // Params
+ int m_rotaryId;
+ QString m_rotaryUuid;
+
+ int m_switchId;
+ QString m_switchUuid;
+
+ // States
+ QString m_lastUpdate;
+ double m_level = 0;
+ int m_batteryLevel = 0;
+ int m_lastButtonCode = -1;
+ int m_lastRotationCode = 0;
+
+signals:
+ void levelChanged(double level);
+ void batteryLevelChanged(int batteryLevel);
+ void buttonPressed(int buttonCode);
+ void rotated(int rotationCode);
+
+};
+
+#endif // HUETAPDIAL_H
diff --git a/philipshue/integrationpluginphilipshue.cpp b/philipshue/integrationpluginphilipshue.cpp
index 37efc1ce..a89b3bfc 100644
--- a/philipshue/integrationpluginphilipshue.cpp
+++ b/philipshue/integrationpluginphilipshue.cpp
@@ -519,18 +519,21 @@ void IntegrationPluginPhilipsHue::setupThing(ThingSetupInfo *info)
if (thing->thingClassId() == tapDialThingClassId) {
qCDebug(dcPhilipsHue) << "Setup Hue Tap Dial" << thing->params() << thing->thingClassId();
- HueRemote *hueTapDial = new HueRemote(bridge, this);
+ HueTapDial *hueTapDial = new HueTapDial(bridge, this);
- hueTapDial->setId(thing->paramValue(tapDialThingSensorIdParamTypeId).toInt());
hueTapDial->setModelId(thing->paramValue(tapDialThingModelIdParamTypeId).toString());
- hueTapDial->setType(thing->paramValue(tapDialThingTypeParamTypeId).toString());
hueTapDial->setUuid(thing->paramValue(tapDialThingUuidParamTypeId).toString());
+ hueTapDial->setRotaryId(thing->paramValue(tapDialThingIdRotaryParamTypeId).toInt());
+ hueTapDial->setRotaryUuid(thing->paramValue(tapDialThingUuidRotaryParamTypeId).toString());
+ hueTapDial->setSwitchId(thing->paramValue(tapDialThingIdSwitchParamTypeId).toInt());
+ hueTapDial->setSwitchUuid(thing->paramValue(tapDialThingUuidSwitchParamTypeId).toString());
- connect(hueTapDial, &HueRemote::stateChanged, this, &IntegrationPluginPhilipsHue::remoteStateChanged);
- connect(hueTapDial, &HueRemote::buttonPressed, this, &IntegrationPluginPhilipsHue::onRemoteButtonEvent);
- connect(hueTapDial, &HueRemote::rotated, this, &IntegrationPluginPhilipsHue::onRemoteRotaryEvent);
+ connect(hueTapDial, &HueTapDial::reachableChanged, this, &IntegrationPluginPhilipsHue::onTapDialReachableChanged);
+ connect(hueTapDial, &HueTapDial::batteryLevelChanged, this, &IntegrationPluginPhilipsHue::onTapDialBatteryLevelChanged);
+ connect(hueTapDial, &HueTapDial::buttonPressed, this, &IntegrationPluginPhilipsHue::onTapDialButtonEvent);
+ connect(hueTapDial, &HueTapDial::rotated, this, &IntegrationPluginPhilipsHue::onTapDialRotaryEvent);
- m_remotes.insert(hueTapDial, thing);
+ m_tapDials.insert(hueTapDial, thing);
return info->finish(Thing::ThingErrorNoError);
}
@@ -707,12 +710,18 @@ void IntegrationPluginPhilipsHue::thingRemoved(Thing *thing)
light->deleteLater();
}
- if (thing->thingClassId() == remoteThingClassId || thing->thingClassId() == dimmerSwitch2ThingClassId || thing->thingClassId() == tapDialThingClassId || thing->thingClassId() == tapThingClassId || thing->thingClassId() == fohThingClassId || thing->thingClassId() == smartButtonThingClassId || thing->thingClassId() == wallSwitchThingClassId) {
+ if (thing->thingClassId() == remoteThingClassId || thing->thingClassId() == dimmerSwitch2ThingClassId || thing->thingClassId() == tapThingClassId || thing->thingClassId() == fohThingClassId || thing->thingClassId() == smartButtonThingClassId || thing->thingClassId() == wallSwitchThingClassId) {
HueRemote *remote = m_remotes.key(thing);
m_remotes.remove(remote);
remote->deleteLater();
}
+ if (thing->thingClassId() == tapDialThingClassId) {
+ HueTapDial *tapDial = m_tapDials.key(thing);
+ m_tapDials.remove(tapDial);
+ tapDial->deleteLater();
+ }
+
if (thing->thingClassId() == outdoorSensorThingClassId || thing->thingClassId() == motionSensorThingClassId) {
HueMotionSensor *motionSensor = m_motionSensors.key(thing);
m_motionSensors.remove(motionSensor);
@@ -1340,49 +1349,6 @@ void IntegrationPluginPhilipsHue::onRemoteButtonEvent(int buttonCode)
// * codes ending in 0 (e.g. 1000) indicate start of long press
// * codes ending in 3 (e.g. 1003) indicate end of long press
// * codes ending in 1 (e.g. 1001) are sent during the long press
- } else if (thing->thingClassId() == tapDialThingClassId) {
- switch (buttonCode) {
- case 1002:
- param = Param(tapDialPressedEventButtonNameParamTypeId, "•");
- id = tapDialPressedEventTypeId;
- break;
- case 1001:
- param = Param(tapDialLongPressedEventButtonNameParamTypeId, "•");
- id = tapDialLongPressedEventTypeId;
- break;
- case 2002:
- param = Param(tapDialPressedEventButtonNameParamTypeId, "••");
- id = tapDialPressedEventTypeId;
- break;
- case 2001:
- param = Param(tapDialLongPressedEventButtonNameParamTypeId, "••");
- id = tapDialLongPressedEventTypeId;
- break;
- case 3002:
- param = Param(tapDialPressedEventButtonNameParamTypeId, "•••");
- id = tapDialPressedEventTypeId;
- break;
- case 3001:
- param = Param(tapDialLongPressedEventButtonNameParamTypeId, "•••");
- id = tapDialLongPressedEventTypeId;
- break;
- case 4002:
- param = Param(tapDialPressedEventButtonNameParamTypeId, "••••");
- id = tapDialPressedEventTypeId;
- break;
- case 4001:
- param = Param(tapDialLongPressedEventButtonNameParamTypeId, "••••");
- id = tapDialLongPressedEventTypeId;
- break;
- default:
- qCDebug(dcPhilipsHue()) << "Unhandled button code received from Hue Tap Dial:" << buttonCode << "Thing name:" << thing->name();
- return;
- }
- // codes ending in 2 (e.g. 1002) are short presses;
- // for long presses the Dimmer Switch V2 sends 3 codes:
- // * codes ending in 0 (e.g. 1000) indicate start of long press
- // * codes ending in 3 (e.g. 1003) indicate end of long press --> not yet supported by this plugin, but e.g. LongPressEnded action could be added
- // * codes ending in 1 (e.g. 1001) are sent during the long press --> probably for backwards compatibility with earlier version, and therefore not added to this plugin
} else if (thing->thingClassId() == tapThingClassId) {
switch (buttonCode) {
case 34:
@@ -1457,15 +1423,75 @@ void IntegrationPluginPhilipsHue::onRemoteButtonEvent(int buttonCode)
emitEvent(Event(id, m_remotes.value(remote)->id(), ParamList() << param));
}
-void IntegrationPluginPhilipsHue::onRemoteRotaryEvent(int rotationCode)
+void IntegrationPluginPhilipsHue::onTapDialButtonEvent(int buttonCode)
{
- HueRemote *remote = static_cast(sender());
- Thing *thing = m_remotes.value(remote);
+ HueTapDial *tapDial = static_cast(sender());
+ Thing *thing = m_tapDials.value(tapDial);
if (!thing) {
qCWarning(dcPhilipsHue()) << "Received a button press event for a thing we don't know!";
return;
}
+ EventTypeId id;
+ Param param;
+
+ if (thing->thingClassId() == tapDialThingClassId) {
+ switch (buttonCode) {
+ case 1002:
+ param = Param(tapDialPressedEventButtonNameParamTypeId, "•");
+ id = tapDialPressedEventTypeId;
+ break;
+ case 1001:
+ param = Param(tapDialLongPressedEventButtonNameParamTypeId, "•");
+ id = tapDialLongPressedEventTypeId;
+ break;
+ case 2002:
+ param = Param(tapDialPressedEventButtonNameParamTypeId, "••");
+ id = tapDialPressedEventTypeId;
+ break;
+ case 2001:
+ param = Param(tapDialLongPressedEventButtonNameParamTypeId, "••");
+ id = tapDialLongPressedEventTypeId;
+ break;
+ case 3002:
+ param = Param(tapDialPressedEventButtonNameParamTypeId, "•••");
+ id = tapDialPressedEventTypeId;
+ break;
+ case 3001:
+ param = Param(tapDialLongPressedEventButtonNameParamTypeId, "•••");
+ id = tapDialLongPressedEventTypeId;
+ break;
+ case 4002:
+ param = Param(tapDialPressedEventButtonNameParamTypeId, "••••");
+ id = tapDialPressedEventTypeId;
+ break;
+ case 4001:
+ param = Param(tapDialLongPressedEventButtonNameParamTypeId, "••••");
+ id = tapDialLongPressedEventTypeId;
+ break;
+ default:
+ qCDebug(dcPhilipsHue()) << "Unhandled button code received from Hue Tap Dial:" << buttonCode << "Thing name:" << thing->name();
+ return;
+ }
+ // codes ending in 2 (e.g. 1002) are short presses;
+ // for long presses the Dimmer Switch V2 sends 3 codes:
+ // * codes ending in 0 (e.g. 1000) indicate start of long press
+ // * codes ending in 3 (e.g. 1003) indicate end of long press --> not yet supported by this plugin, but e.g. LongPressEnded action could be added
+ // * codes ending in 1 (e.g. 1001) are sent during the long press --> probably for backwards compatibility with earlier version, and therefore not added to this plugin
+ }
+ emitEvent(Event(id, m_tapDials.value(tapDial)->id(), ParamList() << param));
+}
+
+
+void IntegrationPluginPhilipsHue::onTapDialRotaryEvent(int rotationCode)
+{
+ HueTapDial *tapDial = static_cast(sender());
+ Thing *thing = m_tapDials.value(tapDial);
+ if (!thing) {
+ qCWarning(dcPhilipsHue()) << "Received a rotary event for a thing we don't know!";
+ return;
+ }
+
EventTypeId id;
Param param;
int currentLevel = thing->stateValue(tapDialLevelStateTypeId).toUInt();
@@ -1491,7 +1517,22 @@ void IntegrationPluginPhilipsHue::onRemoteRotaryEvent(int rotationCode)
return;
}
}
- emitEvent(Event(id, m_remotes.value(remote)->id()));
+ emitEvent(Event(id, m_tapDials.value(tapDial)->id()));
+}
+
+void IntegrationPluginPhilipsHue::onTapDialReachableChanged(bool reachable)
+{
+ HueTapDial *tapDial = static_cast(sender());
+ Thing *tapDialDevice = m_tapDials.value(tapDial);
+ tapDialDevice->setStateValue(tapDialConnectedStateTypeId, reachable);
+}
+
+void IntegrationPluginPhilipsHue::onTapDialBatteryLevelChanged(int batteryLevel)
+{
+ HueTapDial *tapDial = static_cast(sender());
+ Thing *tapDialDevice = m_tapDials.value(tapDial);
+ tapDialDevice->setStateValue(tapDialBatteryLevelStateTypeId, batteryLevel);
+ tapDialDevice->setStateValue(tapDialBatteryCriticalStateTypeId, (batteryLevel < 5));
}
void IntegrationPluginPhilipsHue::onMotionSensorReachableChanged(bool reachable)
@@ -1784,8 +1825,10 @@ void IntegrationPluginPhilipsHue::processBridgeSensorDiscoveryResponse(Thing *th
// Create sensors if not already added
QVariantMap sensorsMap = jsonDoc.toVariant().toMap();
QHash motionSensors;
+ QHash tapDials;
QList remotesToRemove = m_remotes.keys();
QList sensorsToRemove = m_motionSensors.keys();
+ QList tapDialsToRemove = m_tapDials.keys();
foreach (const QString &sensorId, sensorsMap.keys()) {
QVariantMap sensorMap = sensorsMap.value(sensorId).toMap();
@@ -1800,6 +1843,12 @@ void IntegrationPluginPhilipsHue::processBridgeSensorDiscoveryResponse(Thing *th
break;
}
}
+ foreach (HueTapDial* tapDial, tapDialsToRemove) {
+ if (tapDial->uuid() == uuid.split("-").first()) {
+ tapDialsToRemove.removeAll(tapDial);
+ break;
+ }
+ }
foreach (HueMotionSensor* sensor, sensorsToRemove) {
if (sensor->uuid() == uuid.split("-").first()) {
sensorsToRemove.removeAll(sensor);
@@ -1836,26 +1885,49 @@ void IntegrationPluginPhilipsHue::processBridgeSensorDiscoveryResponse(Thing *th
// Tap Dial
} else if (model == "RDM002") {
+ // Get the base uuid from this sensor
+ QString baseUuid = HueDevice::getBaseUuid(uuid);
+ qCDebug(dcPhilipsHue) << "Base uuid:" << baseUuid;
+
+ // Rotary dial
if (sensorMap.value("type").toString() == "ZLLRelativeRotary") {
- ThingDescriptor descriptor(tapDialThingClassId, sensorMap.value("name").toString(), "Philips Hue Tap Dial", thing->id());
- ParamList params;
- params.append(Param(tapDialThingModelIdParamTypeId, model));
- params.append(Param(tapDialThingTypeParamTypeId, sensorMap.value("type").toString()));
- params.append(Param(tapDialThingUuidParamTypeId, uuid));
- params.append(Param(tapDialThingSensorIdParamTypeId, sensorId));
- descriptor.setParams(params);
- emit autoThingsAppeared({descriptor});
- qCDebug(dcPhilipsHue) << "Found new tap dial" << sensorMap.value("name").toString() << model;
- } else if (sensorMap.value("type").toString() == "ZLLSwitch") {
- ThingDescriptor descriptor(tapDialThingClassId, sensorMap.value("name").toString(), "Philips Hue Tap Dial", thing->id());
- ParamList params;
- params.append(Param(tapDialThingModelIdParamTypeId, model));
- params.append(Param(tapDialThingTypeParamTypeId, sensorMap.value("type").toString()));
- params.append(Param(tapDialThingUuidParamTypeId, uuid));
- params.append(Param(tapDialThingSensorIdParamTypeId, sensorId));
- descriptor.setParams(params);
- emit autoThingsAppeared({descriptor});
- qCDebug(dcPhilipsHue) << "Found new tap dial" << sensorMap.value("name").toString() << model;
+ qCDebug(dcPhilipsHue()) << "Found rotary dial from tap dial:" << baseUuid << sensorMap;
+ // Check if we have tap dial for this rotary dial
+ if (tapDials.contains(baseUuid)) {
+ HueTapDial *tapDial = tapDials.value(baseUuid);
+ tapDial->setRotaryUuid(uuid);
+ tapDial->setRotaryId(sensorId.toInt());
+ } else {
+ // Create a tap dial
+ HueTapDial *tapDial = nullptr;
+ tapDial = new HueTapDial(bridge, this);
+
+ tapDial->setModelId(model);
+ tapDial->setUuid(baseUuid);
+ tapDial->setRotaryUuid(uuid);
+ tapDial->setRotaryId(sensorId.toInt());
+ tapDials.insert(baseUuid, tapDial);
+ }
+ }
+ // Buttons
+ if (sensorMap.value("type").toString() == "ZLLSwitch") {
+ qCDebug(dcPhilipsHue()) << "Found switch from tap dial:" << baseUuid << sensorMap;
+ // Check if we have tap dial for this switch
+ if (tapDials.contains(baseUuid)) {
+ HueTapDial *tapDial = tapDials.value(baseUuid);
+ tapDial->setSwitchUuid(uuid);
+ tapDial->setSwitchId(sensorId.toInt());
+ } else {
+ // Create a tap dial
+ HueTapDial *tapDial = nullptr;
+ tapDial = new HueTapDial(bridge, this);
+
+ tapDial->setModelId(model);
+ tapDial->setUuid(baseUuid);
+ tapDial->setSwitchUuid(uuid);
+ tapDial->setSwitchId(sensorId.toInt());
+ tapDials.insert(baseUuid, tapDial);
+ }
}
// Smart Button
@@ -2023,6 +2095,28 @@ void IntegrationPluginPhilipsHue::processBridgeSensorDiscoveryResponse(Thing *th
motionSensor->deleteLater();
}
+ // Create tap dials if there are any new devices found
+ foreach (HueTapDial *tapDial, tapDials.values()) {
+ QString baseUuid = tapDials.key(tapDial);
+ if (tapDial->isValid()) {
+ ThingDescriptor descriptor(tapDialThingClassId, tr("Philips Hue Tap Dial"), baseUuid, thing->id());
+ ParamList params;
+ params.append(Param(tapDialThingModelIdParamTypeId, tapDial->modelId()));
+ params.append(Param(tapDialThingUuidParamTypeId, tapDial->uuid()));
+ params.append(Param(tapDialThingIdRotaryParamTypeId, tapDial->rotaryId()));
+ params.append(Param(tapDialThingUuidRotaryParamTypeId, tapDial->rotaryUuid()));
+ params.append(Param(tapDialThingIdSwitchParamTypeId, tapDial->switchId()));
+ params.append(Param(tapDialThingUuidSwitchParamTypeId, tapDial->switchUuid()));
+ descriptor.setParams(params);
+ qCDebug(dcPhilipsHue()) << "Found new tap dial" << baseUuid << tapDialThingClassId;
+ emit autoThingsAppeared({descriptor});
+ }
+
+ // Clean up
+ tapDials.remove(baseUuid);
+ tapDial->deleteLater();
+ }
+
foreach (HueRemote* remote, remotesToRemove) {
Thing *remoteThing = m_remotes.value(remote);
if (remoteThing->parentId() == thing->id()) {
@@ -2031,6 +2125,14 @@ void IntegrationPluginPhilipsHue::processBridgeSensorDiscoveryResponse(Thing *th
}
}
+ foreach (HueTapDial* tapDial, tapDialsToRemove) {
+ Thing *tapDialThing = m_tapDials.value(tapDial);
+ if (tapDialThing->parentId() == thing->id()) {
+ qCDebug(dcPhilipsHue()) << "Hue tap dial disappeared from bridge";
+ emit autoThingDisappeared(tapDialThing->id());
+ }
+ }
+
foreach (HueMotionSensor* sensor, sensorsToRemove) {
Thing *sensorThing = m_motionSensors.value(sensor);
if (sensorThing->parentId() == thing->id()) {
@@ -2191,6 +2293,13 @@ void IntegrationPluginPhilipsHue::processSensorsRefreshResponse(Thing *thing, co
}
}
+ // Tap dials
+ foreach (HueTapDial *tapDial, m_tapDials.keys()) {
+ if (tapDial->hasSensor(sensorId.toInt()) && m_tapDials.value(tapDial)->parentId() == thing->id()) {
+ tapDial->updateStates(sensorMap);
+ }
+ }
+
// Motion sensors
foreach (HueMotionSensor *motionSensor, m_motionSensors.keys()) {
if (motionSensor->hasSensor(sensorId.toInt()) && m_motionSensors.value(motionSensor)->parentId() == thing->id()) {
@@ -2273,6 +2382,13 @@ void IntegrationPluginPhilipsHue::bridgeReachableChanged(Thing *thing, bool reac
}
}
+ foreach (HueTapDial *tapDial, m_tapDials.keys()) {
+ if (m_tapDials.value(tapDial)->parentId() == thing->id()) {
+ tapDial->setReachable(false);
+ m_tapDials.value(tapDial)->setStateValue(tapDialConnectedStateTypeId, false);
+ }
+ }
+
foreach (HueMotionSensor *motionSensor, m_motionSensors.keys()) {
if (m_motionSensors.value(motionSensor)->parentId() == thing->id()) {
motionSensor->setReachable(false);
@@ -2377,6 +2493,15 @@ bool IntegrationPluginPhilipsHue::sensorAlreadyAdded(const QString &uuid)
}
}
+ // Tap Dial consists out of 2 devices
+ if (thing->thingClassId() == tapDialThingClassId) {
+ if (thing->paramValue(tapDialThingUuidRotaryParamTypeId).toString() == uuid) {
+ return true;
+ } else if (thing->paramValue(tapDialThingUuidSwitchParamTypeId).toString() == uuid) {
+ return true;
+ }
+ }
+
// Outdoor sensor consits out of 3 sensors
if (thing->thingClassId() == outdoorSensorThingClassId) {
if (thing->paramValue(outdoorSensorThingSensorUuidLightParamTypeId).toString() == uuid) {
diff --git a/philipshue/integrationpluginphilipshue.h b/philipshue/integrationpluginphilipshue.h
index 9e5003db..f67d57a3 100644
--- a/philipshue/integrationpluginphilipshue.h
+++ b/philipshue/integrationpluginphilipshue.h
@@ -36,6 +36,7 @@
#include "huelight.h"
#include "hueremote.h"
#include "huemotionsensor.h"
+#include "huetapdial.h"
#include "plugintimer.h"
#include "network/networkaccessmanager.h"
@@ -73,7 +74,13 @@ private slots:
void lightStateChanged();
void remoteStateChanged();
void onRemoteButtonEvent(int buttonCode);
- void onRemoteRotaryEvent(int rotationCode);
+
+ // Tap Dial
+ void onTapDialReachableChanged(bool reachable);
+ void onTapDialBatteryLevelChanged(int batteryLevel);
+ void onTapDialRotaryEvent(int rotationCode);
+ void onTapDialButtonEvent(int buttonCode);
+
// Motion sensor
void onMotionSensorReachableChanged(bool reachable);
@@ -122,6 +129,7 @@ private:
QHash m_bridges;
QHash m_lights;
QHash m_remotes;
+ QHash m_tapDials;
QHash m_motionSensors;
void refreshLight(Thing *thing);
diff --git a/philipshue/integrationpluginphilipshue.json b/philipshue/integrationpluginphilipshue.json
index caeada94..134de668 100644
--- a/philipshue/integrationpluginphilipshue.json
+++ b/philipshue/integrationpluginphilipshue.json
@@ -687,13 +687,6 @@
"type" : "QString",
"readOnly": true
},
- {
- "id": "65748463-dd3f-430d-871c-dd7ee4db0b1c",
- "name": "type",
- "displayName": "type",
- "type" : "QString",
- "readOnly": true
- },
{
"id": "e2365a3c-cdf3-4b1e-b908-e7642e467f20",
"name": "uuid",
@@ -702,11 +695,32 @@
"readOnly": true
},
{
- "id": "9263bef9-4dd9-4658-b798-cd39fcb70fee",
- "name": "sensorId",
- "displayName": "sensor id",
+ "id": "dead7cf0-3ecc-4332-8fc6-42d4e71f508c",
+ "name": "idRotary",
+ "displayName": "Rotary dial id",
"type" : "int",
"readOnly": true
+ },
+ {
+ "id": "267bcb24-b5c1-421d-bc6a-9b8fc7b43696",
+ "name": "uuidRotary",
+ "displayName": "Rotary dial uuid",
+ "type" : "QString",
+ "readOnly": true
+ },
+ {
+ "id": "5269c70c-49e6-4090-aa57-3c89ee328b1e",
+ "name": "idSwitch",
+ "displayName": "Switch id",
+ "type" : "int",
+ "readOnly": true
+ },
+ {
+ "id": "ae7ce90a-9bfb-45e4-90e4-8e491b374249",
+ "name": "uuidSwitch",
+ "displayName": "Switch uuid",
+ "type" : "QString",
+ "readOnly": true
}
],
"settingsTypes": [