add support for the Hue Tap button

This commit is contained in:
Michael Zanetti 2018-03-12 23:55:56 +01:00
parent f07031e589
commit 9d175bcfb9
4 changed files with 188 additions and 22 deletions

View File

@ -2,6 +2,7 @@
* *
* Copyright (C) 2014 Michael Zanetti <michael_zanetti@gmx.net> *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* Copyright (C) 2018 Michael Zanetti <michael.zanetti@guh.io> *
* *
* This file is part of nymea. *
* *
@ -114,8 +115,9 @@ DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *dev
bridge->setName(device->paramValue(hueBridgeBridgeNameParamTypeId).toString());
bridge->setMacAddress(device->paramValue(hueBridgeBridgeMacParamTypeId).toString());
bridge->setZigbeeChannel(device->paramValue(hueBridgeBridgeZigbeeChannelParamTypeId).toInt());
m_bridges.insert(bridge, device);
discoverBridgeDevices(bridge);
return DeviceManager::DeviceSetupStatusSuccess;
}
@ -173,7 +175,7 @@ DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *dev
// hue remote
if (device->deviceClassId() == hueRemoteDeviceClassId) {
qCDebug(dcPhilipsHue) << "Setup Hue remote" << device->params();
qCDebug(dcPhilipsHue) << "Setup Hue remote" << device->params() << device->deviceClassId();
HueRemote *hueRemote = new HueRemote(this);
hueRemote->setId(device->paramValue(hueRemoteSensorIdParamTypeId).toInt());
@ -195,6 +197,24 @@ DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *dev
return DeviceManager::DeviceSetupStatusSuccess;
}
// hue tap
if (device->deviceClassId() == hueTapDeviceClassId) {
HueRemote *hueTap = new HueRemote(this);
hueTap->setName(device->name());
hueTap->setId(device->paramValue(hueTapSensorIdParamTypeId).toInt());
hueTap->setBridgeId(DeviceId(device->paramValue(hueTapBridgeParamTypeId).toString()));
hueTap->setModelId(device->paramValue(hueTapModelIdParamTypeId).toString());
device->setParentId(hueTap->bridgeId());
connect(hueTap, &HueRemote::stateChanged, this, &DevicePluginPhilipsHue::remoteStateChanged);
connect(hueTap, &HueRemote::buttonPressed, this, &DevicePluginPhilipsHue::onRemoteButtonEvent);
m_remotes.insert(hueTap, device);
return DeviceManager::DeviceSetupStatusSuccess;
}
qCWarning(dcPhilipsHue()) << "Unhandled setupDevice call" << device->deviceClassId();
return DeviceManager::DeviceSetupStatusFailure;
}
@ -212,7 +232,7 @@ void DevicePluginPhilipsHue::deviceRemoved(Device *device)
light->deleteLater();
}
if (device->deviceClassId() == hueRemoteDeviceClassId) {
if (device->deviceClassId() == hueRemoteDeviceClassId || device->deviceClassId() == hueTapDeviceClassId) {
HueRemote *remote = m_remotes.key(device);
m_remotes.remove(remote);
remote->deleteLater();
@ -366,7 +386,7 @@ void DevicePluginPhilipsHue::networkManagerReplyReady()
// check HTTP status code
if (status != 200 || reply->error() != QNetworkReply::NoError) {
if (device->stateValue(hueRemoteConnectedStateTypeId).toBool()) {
if (device->stateValue(hueRemoteConnectedStateTypeId).toBool() || device->stateValue(hueTapConnectedStateTypeId).toBool()) {
qCWarning(dcPhilipsHue) << "Refresh Hue sensors request error:" << status << reply->errorString();
bridgeReachableChanged(device, false);
}
@ -399,6 +419,8 @@ void DevicePluginPhilipsHue::networkManagerReplyReady()
return;
}
processSetNameResponse(device, reply->readAll());
} else {
qCWarning(dcPhilipsHue()) << "Unhandled bridge reply" << reply->readAll();
}
reply->deleteLater();
}
@ -548,18 +570,27 @@ void DevicePluginPhilipsHue::remoteStateChanged()
qCWarning(dcPhilipsHue) << "Could not find device for remote" << remote->name();
return;
}
device->setStateValue(hueRemoteConnectedStateTypeId, remote->reachable());
device->setStateValue(hueRemoteBatteryLevelStateTypeId, remote->battery());
device->setStateValue(hueRemoteBatteryCriticalStateTypeId, remote->battery() < 5);
if (device->deviceClassId() == hueTapDeviceClassId) {
device->setStateValue(hueTapConnectedStateTypeId, remote->reachable());
} else {
device->setStateValue(hueRemoteConnectedStateTypeId, remote->reachable());
device->setStateValue(hueRemoteBatteryLevelStateTypeId, remote->battery());
device->setStateValue(hueRemoteBatteryCriticalStateTypeId, remote->battery() < 5);
}
}
void DevicePluginPhilipsHue::onRemoteButtonEvent(const int &buttonCode)
{
HueRemote *remote = static_cast<HueRemote *>(sender());
Device *device = m_remotes.value(remote);
Param param(hueRemoteButtonNameParamTypeId);
EventTypeId id;
Param param;
if (device->deviceClassId() == hueRemoteDeviceClassId) {
param = Param(hueRemoteButtonNameParamTypeId);
} else if (device->deviceClassId() == hueTapDeviceClassId) {
param = Param(hueTapButtonNameParamTypeId);
}
// TODO: Legacy events should be removed eventually
switch (buttonCode) {
@ -603,6 +634,22 @@ void DevicePluginPhilipsHue::onRemoteButtonEvent(const int &buttonCode)
param.setValue("OFF");
id = hueRemoteLongPressedEventTypeId;
break;
case HueRemote::TapButton1Pressed:
param.setValue("");
id = hueTapPressedEventTypeId;
break;
case HueRemote::TapButton2Pressed:
param.setValue("••");
id = hueTapPressedEventTypeId;
break;
case HueRemote::TapButton3Pressed:
param.setValue("•••");
id = hueTapPressedEventTypeId;
break;
case HueRemote::TapButton4Pressed:
param.setValue("•••••");
id = hueTapPressedEventTypeId;
break;
default:
break;
}
@ -879,7 +926,6 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device
}
// create sensors if not already added
QList<DeviceDescriptor> sensorDescriptors;
QVariantMap sensorsMap = jsonDoc.toVariant().toMap();
foreach (QString sensorId, sensorsMap.keys()) {
QVariantMap sensorMap = sensorsMap.value(sensorId).toMap();
@ -890,7 +936,6 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device
if (sensorAlreadyAdded(uuid))
continue;
// check if this is a white light
if (model == "RWL021" || model == "RWL020") {
DeviceDescriptor descriptor(hueRemoteDeviceClassId, "Philips Hue Remote", sensorMap.value("name").toString());
ParamList params;
@ -903,14 +948,22 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device
params.append(Param(hueRemoteUuidParamTypeId, uuid));
params.append(Param(hueRemoteSensorIdParamTypeId, sensorId));
descriptor.setParams(params);
sensorDescriptors.append(descriptor);
emit autoDevicesAppeared(hueRemoteDeviceClassId, {descriptor});
qCDebug(dcPhilipsHue) << "Found new remote" << sensorMap.value("name").toString() << model;
} else if (model == "ZGPSWITCH") {
DeviceDescriptor descriptor(hueTapDeviceClassId, "Hue Tap", sensorMap.value("name").toString());
ParamList params;
params.append(Param(hueTapBridgeParamTypeId, device->id().toString()));
params.append(Param(hueTapUuidParamTypeId, uuid));
params.append(Param(hueTapModelIdParamTypeId, model));
params.append(Param(hueTapSensorIdParamTypeId, sensorId));
descriptor.setParams(params);
emit autoDevicesAppeared(hueTapDeviceClassId, {descriptor});
qCDebug(dcPhilipsHue()) << "Found hue tap:" << sensorMap << hueTapDeviceClassId;
} else {
qCDebug(dcPhilipsHue()) << "Found unknown sensor:" << model;
}
}
if (!sensorDescriptors.isEmpty())
emit autoDevicesAppeared(hueRemoteDeviceClassId, sensorDescriptors);
}
void DevicePluginPhilipsHue::processLightRefreshResponse(Device *device, const QByteArray &data)
@ -1045,7 +1098,7 @@ void DevicePluginPhilipsHue::processSensorsRefreshResponse(Device *device, const
QVariantMap sensorMap = sensorsMap.value(sensorId).toMap();
foreach (HueRemote *remote, m_remotes.keys()) {
if (remote->id() == sensorId.toInt() && remote->bridgeId() == device->id()) {
//qCDebug(dcPhilipsHue) << "update remote" << remote->id() << remote->name();
qCDebug(dcPhilipsHue) << "update remote" << remote->id() << remote->name();
remote->updateStates(sensorMap.value("state").toMap(), sensorMap.value("config").toMap());
}
}
@ -1225,7 +1278,11 @@ void DevicePluginPhilipsHue::bridgeReachableChanged(Device *device, const bool &
foreach (HueRemote *remote, m_remotes.keys()) {
if (remote->bridgeId() == device->id()) {
remote->setReachable(false);
m_remotes.value(remote)->setStateValue(hueRemoteConnectedStateTypeId, false);
if (m_remotes.value(remote)->deviceClassId() == hueRemoteDeviceClassId) {
m_remotes.value(remote)->setStateValue(hueRemoteConnectedStateTypeId, false);
} else if (m_remotes.value(remote)->deviceClassId() == hueTapDeviceClassId) {
m_remotes.value(remote)->setStateValue(hueTapConnectedStateTypeId, false);
}
}
}
}
@ -1269,6 +1326,11 @@ bool DevicePluginPhilipsHue::sensorAlreadyAdded(const QString &uuid)
return true;
}
}
if (device->deviceClassId() == hueTapDeviceClassId) {
if (device->paramValue(hueTapUuidParamTypeId).toString() == uuid) {
return true;
}
}
}
return false;
}

View File

@ -596,7 +596,7 @@
"name": "buttonName",
"displayName": "Button name",
"type": "QString",
"possibleValues": ["ON", "OFF", "DIM UP", "DIM DOWN"]
"allowedValues": ["ON", "OFF", "DIM UP", "DIM DOWN"]
}
]
},
@ -610,7 +610,101 @@
"name": "buttonName",
"displayName": "Button name",
"type": "QString",
"possibleValues": ["ON", "OFF", "DIM UP", "DIM DOWN"]
"allowedValues": ["ON", "OFF", "DIM UP", "DIM DOWN"]
}
]
}
]
},
{
"id": "2b8c1fb8-67ee-42e9-947b-16e0a09f0d4e",
"name": "hueTap",
"displayName": "Hue Tap",
"deviceIcon": "Switch",
"interfaces": ["simplemultibutton", "connectable"],
"createMethods": ["auto"],
"paramTypes": [
{
"id": "3d450f00-b521-4a2c-985f-046fad5122cb",
"name": "name",
"displayName": "name",
"type" : "QString",
"inputType": "TextLine"
},
{
"id": "c9e7483b-2f4e-4280-a437-8471f4dc9b22",
"name": "bridge",
"displayName": "bridge",
"type" : "QString",
"readOnly": true
},
{
"id": "62d92175-db3a-4da2-a72b-f58f34cb6911",
"name": "modelId",
"displayName": "model id",
"type" : "QString",
"readOnly": true
},
{
"id": "eace85b9-5369-466f-89eb-46c4de718305",
"name": "type",
"displayName": "type",
"type" : "QString",
"readOnly": true
},
{
"id": "25cf4167-6c28-4497-9fa9-3d02faf4f3ed",
"name": "uuid",
"displayName": "uuid",
"type" : "QString",
"readOnly": true
},
{
"id": "fb507641-9c78-4a28-937d-711775454e90",
"name": "apiKey",
"displayName": "api key",
"type" : "QString",
"inputType": "TextLine",
"readOnly": true
},
{
"id": "6be56a1b-f64a-4188-a052-1fab11e01718",
"name": "host",
"displayName": "host address",
"type" : "QString",
"inputType": "IPv4Address",
"readOnly": true
},
{
"id": "5eca2b24-8986-4487-bc12-50e91d023d97",
"name": "sensorId",
"displayName": "sensor id",
"type" : "int",
"readOnly": true
}
],
"stateTypes": [
{
"id": "5e21b032-1230-4e93-8543-0c4773da17d3",
"name": "connected",
"displayName": "reachable",
"displayNameEvent": "reachable changed",
"defaultValue": false,
"type": "bool"
}
],
"eventTypes": [
{
"id": "c45dd703-7cbd-48f7-88dc-31045cc3d39c",
"name": "pressed",
"displayName": "Button pressed",
"paramTypes": [
{
"id": "8ed643c0-1b8a-4709-8abf-717cf213f4a4",
"name": "buttonName",
"displayName": "Button name",
"type": "QString",
"allowedValues": ["•", "••", "•••", "••••"]
}
]
}

View File

@ -40,7 +40,13 @@ void HueRemote::setBattery(const int &battery)
void HueRemote::updateStates(const QVariantMap &statesMap, const QVariantMap &configMap)
{
setReachable(configMap.value("reachable", false).toBool());
if (configMap.contains("reachable")) {
setReachable(configMap.value("reachable", false).toBool());
} else {
// Hue Tap doesn't have a reachable property as it's a ultra low power device. Let's mark it reachable by default as we only
// get this response if the bridge is reachable.
setReachable(true);
}
setBattery(configMap.value("battery", 0).toInt());
emit stateChanged();

View File

@ -44,7 +44,11 @@ public:
DimDownLongPressed = 3001,
DimDownPressed = 3002,
OffLongPressed = 4001,
OffPressed = 4002
OffPressed = 4002,
TapButton1Pressed = 34,
TapButton2Pressed = 16,
TapButton3Pressed = 17,
TapButton4Pressed = 18
};
explicit HueRemote(QObject *parent = 0);