Merge PR #692: Philips Hue: Add support for Hue Tap Dial

This commit is contained in:
jenkins 2023-05-16 12:59:21 +02:00
commit fbd40e05b2
17 changed files with 3773 additions and 5337 deletions

View File

@ -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

View File

@ -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
View 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
View 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

View File

@ -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();
}
}
}
}

View File

@ -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

View File

@ -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 @@
]
}
]
}
}

View File

@ -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