Merge PR #692: Philips Hue: Add support for Hue Tap Dial
This commit is contained in:
commit
fbd40e05b2
@ -10,6 +10,7 @@ This plugin allows to interact with the Hue bridge. Each light bulb, sensor and
|
||||
* No internet or cloud connection required
|
||||
* Hue Dimmer switch V1 and V2
|
||||
* Hue Tap Switch
|
||||
* Hue Tap Dial Switch
|
||||
* Friends of Hue Switch (e.g. Niko, ...)
|
||||
* Hue Smart Button
|
||||
* Hue Wall Switch Module
|
||||
|
||||
@ -62,7 +62,7 @@ void HueRemote::updateStates(const QVariantMap &statesMap, const QVariantMap &co
|
||||
QString lastUpdate = statesMap.value("lastupdated").toString();
|
||||
int buttonCode = statesMap.value("buttonevent").toInt();
|
||||
|
||||
// If we never polled, just store lastUpdate/buttonCode and not emit a falsely button pressed event
|
||||
// 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;
|
||||
|
||||
162
philipshue/huetapdial.cpp
Normal file
162
philipshue/huetapdial.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <QtMath>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int 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());
|
||||
}
|
||||
|
||||
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 lastUpdateRotation = 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_lastUpdateRotation.isEmpty() || m_lastRotationCode == 0) {
|
||||
m_lastUpdateRotation = lastUpdateRotation;
|
||||
m_lastRotationCode = rotationCode;
|
||||
}
|
||||
if (m_lastUpdateRotation != lastUpdateRotation || m_lastRotationCode != rotationCode) {
|
||||
m_lastUpdateRotation = lastUpdateRotation;
|
||||
m_lastRotationCode = rotationCode;
|
||||
qCDebug(dcPhilipsHue) << "rotated" << rotationCode;
|
||||
emit rotated(rotationCode);
|
||||
}
|
||||
}
|
||||
|
||||
// If button press
|
||||
if (sensorMap.value("uniqueid").toString() == m_switchUuid) {
|
||||
QString lastUpdateButton = 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_lastUpdateButton.isEmpty() || m_lastButtonCode == -1) {
|
||||
m_lastUpdateButton = lastUpdateButton;
|
||||
m_lastButtonCode = buttonCode;
|
||||
}
|
||||
if (m_lastUpdateButton != lastUpdateButton || m_lastButtonCode != buttonCode) {
|
||||
m_lastUpdateButton = lastUpdateButton;
|
||||
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;
|
||||
}
|
||||
92
philipshue/huetapdial.h
Normal file
92
philipshue/huetapdial.h
Normal file
@ -0,0 +1,92 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#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);
|
||||
|
||||
private:
|
||||
// Params
|
||||
int m_rotaryId;
|
||||
QString m_rotaryUuid;
|
||||
|
||||
int m_switchId;
|
||||
QString m_switchUuid;
|
||||
|
||||
// States
|
||||
QString m_lastUpdateButton;
|
||||
QString m_lastUpdateRotation;
|
||||
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
|
||||
@ -515,6 +515,28 @@ void IntegrationPluginPhilipsHue::setupThing(ThingSetupInfo *info)
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
|
||||
// Hue Tap Dial
|
||||
if (thing->thingClassId() == tapDialThingClassId) {
|
||||
qCDebug(dcPhilipsHue) << "Setup Hue Tap Dial" << thing->params() << thing->thingClassId();
|
||||
|
||||
HueTapDial *hueTapDial = new HueTapDial(bridge, this);
|
||||
|
||||
hueTapDial->setModelId(thing->paramValue(tapDialThingModelIdParamTypeId).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, &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_tapDials.insert(hueTapDial, thing);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
|
||||
// Hue tap
|
||||
if (thing->thingClassId() == tapThingClassId) {
|
||||
HueRemote *hueTap = new HueRemote(bridge, this);
|
||||
@ -688,12 +710,18 @@ void IntegrationPluginPhilipsHue::thingRemoved(Thing *thing)
|
||||
light->deleteLater();
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == remoteThingClassId || thing->thingClassId() == dimmerSwitch2ThingClassId|| 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);
|
||||
@ -1209,6 +1237,10 @@ void IntegrationPluginPhilipsHue::remoteStateChanged()
|
||||
thing->setStateValue(dimmerSwitch2ConnectedStateTypeId, remote->reachable());
|
||||
thing->setStateValue(dimmerSwitch2BatteryLevelStateTypeId, remote->battery());
|
||||
thing->setStateValue(dimmerSwitch2BatteryCriticalStateTypeId, remote->battery() < 5);
|
||||
} else if (thing->thingClassId() == tapDialThingClassId) {
|
||||
thing->setStateValue(tapDialConnectedStateTypeId, remote->reachable());
|
||||
thing->setStateValue(tapDialBatteryLevelStateTypeId, remote->battery());
|
||||
thing->setStateValue(tapDialBatteryCriticalStateTypeId, remote->battery() < 5);
|
||||
} else if (thing->thingClassId() == tapThingClassId) {
|
||||
thing->setStateValue(tapConnectedStateTypeId, remote->reachable());
|
||||
} else if (thing->thingClassId() == fohThingClassId) {
|
||||
@ -1391,6 +1423,113 @@ void IntegrationPluginPhilipsHue::onRemoteButtonEvent(int buttonCode)
|
||||
emitEvent(Event(id, m_remotes.value(remote)->id(), ParamList() << param));
|
||||
}
|
||||
|
||||
void IntegrationPluginPhilipsHue::onTapDialButtonEvent(int buttonCode)
|
||||
{
|
||||
HueTapDial *tapDial = static_cast<HueTapDial *>(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;
|
||||
}
|
||||
}
|
||||
emitEvent(Event(id, m_tapDials.value(tapDial)->id(), ParamList() << param));
|
||||
}
|
||||
|
||||
|
||||
void IntegrationPluginPhilipsHue::onTapDialRotaryEvent(int rotationCode)
|
||||
{
|
||||
HueTapDial *tapDial = static_cast<HueTapDial *>(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();
|
||||
int stepSize = thing->setting(tapDialSettingsStepSizeParamTypeId).toUInt();
|
||||
int largeStepSize = thing->setting(tapDialSettingsLargeStepSizeParamTypeId).toUInt();
|
||||
|
||||
if (thing->thingClassId() == tapDialThingClassId) {
|
||||
qCDebug(dcPhilipsHue()) << "Rotation code received from Hue Tap Dial:" << rotationCode << "Thing name:" << thing->name();
|
||||
if (rotationCode == 15) {
|
||||
id = tapDialIncreaseEventTypeId;
|
||||
thing->setStateValue(tapDialLevelStateTypeId, qMin(100, currentLevel + stepSize));
|
||||
} else if (rotationCode == -15) {
|
||||
id = tapDialDecreaseEventTypeId;
|
||||
thing->setStateValue(tapDialLevelStateTypeId, qMax(0, currentLevel - stepSize));
|
||||
} else if (rotationCode > 15) {
|
||||
id = tapDialLargeIncreaseEventTypeId;
|
||||
thing->setStateValue(tapDialLevelStateTypeId, qMin(100, currentLevel + largeStepSize));
|
||||
} else if (rotationCode < -15) {
|
||||
id = tapDialLargeDecreaseEventTypeId;
|
||||
thing->setStateValue(tapDialLevelStateTypeId, qMax(0, currentLevel - largeStepSize));
|
||||
} else {
|
||||
qCDebug(dcPhilipsHue()) << "Unhandled rotation code received from Hue Tap Dial:" << rotationCode << "Thing name:" << thing->name();
|
||||
return;
|
||||
}
|
||||
}
|
||||
emitEvent(Event(id, m_tapDials.value(tapDial)->id()));
|
||||
}
|
||||
|
||||
void IntegrationPluginPhilipsHue::onTapDialReachableChanged(bool reachable)
|
||||
{
|
||||
HueTapDial *tapDial = static_cast<HueTapDial *>(sender());
|
||||
Thing *tapDialDevice = m_tapDials.value(tapDial);
|
||||
tapDialDevice->setStateValue(tapDialConnectedStateTypeId, reachable);
|
||||
}
|
||||
|
||||
void IntegrationPluginPhilipsHue::onTapDialBatteryLevelChanged(int batteryLevel)
|
||||
{
|
||||
HueTapDial *tapDial = static_cast<HueTapDial *>(sender());
|
||||
Thing *tapDialDevice = m_tapDials.value(tapDial);
|
||||
tapDialDevice->setStateValue(tapDialBatteryLevelStateTypeId, batteryLevel);
|
||||
tapDialDevice->setStateValue(tapDialBatteryCriticalStateTypeId, (batteryLevel < 5));
|
||||
}
|
||||
|
||||
void IntegrationPluginPhilipsHue::onMotionSensorReachableChanged(bool reachable)
|
||||
{
|
||||
HueMotionSensor *sensor = static_cast<HueMotionSensor *>(sender());
|
||||
@ -1557,7 +1696,7 @@ void IntegrationPluginPhilipsHue::processBridgeLightDiscoveryResponse(Thing *thi
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcPhilipsHue()) << "Lights on bridge:" << qUtf8Printable(jsonDoc.toJson());
|
||||
// qCDebug(dcPhilipsHue()) << "Lights on bridge:" << qUtf8Printable(jsonDoc.toJson());
|
||||
|
||||
// Create Lights if not already added
|
||||
ThingDescriptors descriptors;
|
||||
@ -1676,11 +1815,15 @@ void IntegrationPluginPhilipsHue::processBridgeSensorDiscoveryResponse(Thing *th
|
||||
return;
|
||||
}
|
||||
|
||||
// qCDebug(dcPhilipsHue()) << "Sensors on bridge:" << qUtf8Printable(jsonDoc.toJson());
|
||||
|
||||
// Create sensors if not already added
|
||||
QVariantMap sensorsMap = jsonDoc.toVariant().toMap();
|
||||
QHash<QString, HueMotionSensor *> motionSensors;
|
||||
QHash<QString, HueTapDial *> tapDials;
|
||||
QList<HueRemote*> remotesToRemove = m_remotes.keys();
|
||||
QList<HueMotionSensor*> sensorsToRemove = m_motionSensors.keys();
|
||||
QList<HueTapDial*> tapDialsToRemove = m_tapDials.keys();
|
||||
foreach (const QString &sensorId, sensorsMap.keys()) {
|
||||
|
||||
QVariantMap sensorMap = sensorsMap.value(sensorId).toMap();
|
||||
@ -1695,6 +1838,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);
|
||||
@ -1729,6 +1878,53 @@ void IntegrationPluginPhilipsHue::processBridgeSensorDiscoveryResponse(Thing *th
|
||||
emit autoThingsAppeared({descriptor});
|
||||
qCDebug(dcPhilipsHue) << "Found new dimmer switch v2" << sensorMap.value("name").toString() << model;
|
||||
|
||||
// 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") {
|
||||
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
|
||||
} else if (model == "ROM001") {
|
||||
ThingDescriptor descriptor(smartButtonThingClassId, sensorMap.value("name").toString(), "Philips Hue Smart Button", thing->id());
|
||||
@ -1894,6 +2090,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()) {
|
||||
@ -1902,6 +2120,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()) {
|
||||
@ -2062,6 +2288,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()) {
|
||||
@ -2130,6 +2363,8 @@ void IntegrationPluginPhilipsHue::bridgeReachableChanged(Thing *thing, bool reac
|
||||
m_remotes.value(remote)->setStateValue(remoteConnectedStateTypeId, false);
|
||||
} else if (m_remotes.value(remote)->thingClassId() == dimmerSwitch2ThingClassId) {
|
||||
m_remotes.value(remote)->setStateValue(dimmerSwitch2ConnectedStateTypeId, false);
|
||||
} else if (m_remotes.value(remote)->thingClassId() == tapDialThingClassId) {
|
||||
m_remotes.value(remote)->setStateValue(tapDialConnectedStateTypeId, false);
|
||||
} else if (m_remotes.value(remote)->thingClassId() == tapThingClassId) {
|
||||
m_remotes.value(remote)->setStateValue(tapConnectedStateTypeId, false);
|
||||
} else if (m_remotes.value(remote)->thingClassId() == fohThingClassId) {
|
||||
@ -2142,6 +2377,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);
|
||||
@ -2211,6 +2453,13 @@ bool IntegrationPluginPhilipsHue::sensorAlreadyAdded(const QString &uuid)
|
||||
}
|
||||
}
|
||||
|
||||
// Hue tap dial
|
||||
if (thing->thingClassId() == tapDialThingClassId) {
|
||||
if (thing->paramValue(tapDialThingUuidParamTypeId).toString() == uuid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Hue tap
|
||||
if (thing->thingClassId() == tapThingClassId) {
|
||||
if (thing->paramValue(tapThingUuidParamTypeId).toString() == uuid) {
|
||||
@ -2239,6 +2488,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) {
|
||||
@ -2281,4 +2539,4 @@ void IntegrationPluginPhilipsHue::abortRequests(QHash<QNetworkReply *, Thing *>
|
||||
reply->abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -36,6 +36,7 @@
|
||||
#include "huelight.h"
|
||||
#include "hueremote.h"
|
||||
#include "huemotionsensor.h"
|
||||
#include "huetapdial.h"
|
||||
|
||||
#include "plugintimer.h"
|
||||
#include "network/networkaccessmanager.h"
|
||||
@ -74,6 +75,13 @@ private slots:
|
||||
void remoteStateChanged();
|
||||
void onRemoteButtonEvent(int buttonCode);
|
||||
|
||||
// Tap Dial
|
||||
void onTapDialReachableChanged(bool reachable);
|
||||
void onTapDialBatteryLevelChanged(int batteryLevel);
|
||||
void onTapDialRotaryEvent(int rotationCode);
|
||||
void onTapDialButtonEvent(int buttonCode);
|
||||
|
||||
|
||||
// Motion sensor
|
||||
void onMotionSensorReachableChanged(bool reachable);
|
||||
void onMotionSensorBatteryLevelChanged(int batteryLevel);
|
||||
@ -121,6 +129,7 @@ private:
|
||||
QHash<HueBridge *, Thing *> m_bridges;
|
||||
QHash<HueLight *, Thing *> m_lights;
|
||||
QHash<HueRemote *, Thing *> m_remotes;
|
||||
QHash<HueTapDial *, Thing *> m_tapDials;
|
||||
QHash<HueMotionSensor *, Thing *> m_motionSensors;
|
||||
|
||||
void refreshLight(Thing *thing);
|
||||
@ -155,4 +164,4 @@ private:
|
||||
void abortRequests(QHash<QNetworkReply *, Thing *> requestList, Thing* thing);
|
||||
};
|
||||
|
||||
#endif // INTEGRATIONPLUGINPHILIPSHUE_H
|
||||
#endif // INTEGRATIONPLUGINPHILIPSHUE_H
|
||||
@ -673,6 +673,168 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "58e579b4-f917-478c-9006-f1f8d4df2ded",
|
||||
"name": "tapDial",
|
||||
"displayName": "Hue Tap Dial",
|
||||
"interfaces": ["longpressmultibutton", "battery", "wirelessconnectable"],
|
||||
"createMethods": ["auto"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "de566aaa-2a5f-4e31-bce1-5924014fbd6a",
|
||||
"name": "modelId",
|
||||
"displayName": "model id",
|
||||
"type" : "QString",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "e2365a3c-cdf3-4b1e-b908-e7642e467f20",
|
||||
"name": "uuid",
|
||||
"displayName": "uuid",
|
||||
"type" : "QString",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
{
|
||||
"id": "045f8a6a-e8de-4780-a4d3-60a986130977",
|
||||
"name": "stepSize",
|
||||
"displayName": "Step size",
|
||||
"type": "uint",
|
||||
"defaultValue": 5,
|
||||
"minValue": 1,
|
||||
"maxValue": 50
|
||||
},
|
||||
{
|
||||
"id": "30342d23-b2c1-4969-85f1-1b0f58eeb8ce",
|
||||
"name": "largeStepSize",
|
||||
"displayName": "Large Step size",
|
||||
"type": "uint",
|
||||
"defaultValue": 10,
|
||||
"minValue": 1,
|
||||
"maxValue": 50
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "7c81af92-3643-4211-9d62-407cbc596619",
|
||||
"name": "connected",
|
||||
"displayName": "reachable",
|
||||
"displayNameEvent": "reachable changed",
|
||||
"defaultValue": false,
|
||||
"type": "bool",
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "9d0d190f-2898-4c9a-a423-ec7187bfcb5e",
|
||||
"name": "batteryLevel",
|
||||
"displayName": "battery",
|
||||
"displayNameEvent": "battery changed",
|
||||
"type": "int",
|
||||
"unit": "Percentage",
|
||||
"defaultValue": 0,
|
||||
"minValue": 0,
|
||||
"maxValue": 100
|
||||
},
|
||||
{
|
||||
"id": "79efe477-2757-4457-bdfb-a3fbeb198e7a",
|
||||
"name": "batteryCritical",
|
||||
"displayName": "battery critical",
|
||||
"displayNameEvent": "battery critical changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "23528402-b98f-49b8-90cf-7b68f251ad59",
|
||||
"name": "level",
|
||||
"displayName": "Level",
|
||||
"displayNameEvent": "Level changed",
|
||||
"unit": "Percentage",
|
||||
"type": "uint",
|
||||
"defaultValue": 0,
|
||||
"minValue": 0,
|
||||
"maxValue": 100
|
||||
}
|
||||
],
|
||||
"eventTypes": [
|
||||
{
|
||||
"id": "9ff8853f-b1fe-44ac-bcfe-193303397012",
|
||||
"name": "pressed",
|
||||
"displayName": "Button pressed",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "73863657-b7fa-4c83-827c-6dec46278671",
|
||||
"name": "buttonName",
|
||||
"displayName": "Button name",
|
||||
"type": "QString",
|
||||
"allowedValues": ["•", "••", "•••", "••••"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "4c1bac9f-291e-4bd2-aa2e-c8ecc182c300",
|
||||
"name": "longPressed",
|
||||
"displayName": "Button longpress",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "ce015b27-2ffb-4d8b-bb66-63013c4e2f52",
|
||||
"name": "buttonName",
|
||||
"displayName": "Button name",
|
||||
"type": "QString",
|
||||
"allowedValues": ["•", "••", "•••", "••••"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "11edf456-4484-45f3-8638-01162fb47609",
|
||||
"name": "increase",
|
||||
"displayName": "Increase"
|
||||
},
|
||||
{
|
||||
"id": "1b951b52-240d-437c-ae8d-c40298993acb",
|
||||
"name": "decrease",
|
||||
"displayName": "Decrease"
|
||||
},
|
||||
{
|
||||
"id": "8d8aa384-e408-4365-8381-ac763168788b",
|
||||
"name": "largeIncrease",
|
||||
"displayName": "Large Increase"
|
||||
},
|
||||
{
|
||||
"id": "d1415f84-c4cc-478d-89a4-bd7a5c9761e5",
|
||||
"name": "largeDecrease",
|
||||
"displayName": "Large Decrease"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1e34a056-9f37-4741-b249-a5eca7a4ab4e",
|
||||
"name": "smartButton",
|
||||
@ -1310,4 +1472,4 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,8 @@ SOURCES += \
|
||||
huelight.cpp \
|
||||
huemotionsensor.cpp \
|
||||
hueremote.cpp \
|
||||
huedevice.cpp
|
||||
huedevice.cpp \
|
||||
huetapdial.cpp
|
||||
|
||||
HEADERS += \
|
||||
integrationpluginphilipshue.h \
|
||||
@ -23,7 +24,8 @@ HEADERS += \
|
||||
huelight.h \
|
||||
huemotionsensor.h \
|
||||
hueremote.h \
|
||||
huedevice.h
|
||||
huedevice.h \
|
||||
huetapdial.h
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user